如何避免从文件中读取行的 subshell 问题?[复制]

How to avoid subshell issues with reading lines from a file? [duplicate]

提问人:Richard T 提问时间:8/6/2023 最后编辑:Mark RotteveelRichard T 更新时间:8/12/2023 访问量:82

问:

这个问题在这里已经有答案了:
4个月前关闭。

这篇文章是 4 个月前编辑并提交审核的,未能重新打开帖子:

需要更多关注通过编辑这篇文章来更新问题,使其仅关注一个问题。

“回到过去”,1990 年代中期,我不得不编写一个名为 “” 的 C 程序,以避免在进行类似构造时因子壳而产生的全局变量与局部变量问题:readline

while read line
do
   my_var=$(echo "$line" | cut -f 12 -d ":")
   if [ "$my_var" == "$target" ] ;
   then
      found_target=1
   fi
done < some_file

在更新这个问题以希望能解决一些评论时,我意识到另一个我对这种类型的循环一无所知的问题;你如何实现“我们已经找到了目标,我们现在可以停止阅读了!使用这种类型的循环?我猜这应该涉及:

while [ -z "$found_target" ] && 

但是我不知道如何完成这条线!为了“正常工作”,该示例必须在循环中保留found_target和my_var,以便循环后面的代码使用。

请注意,这里的代码示例不是我今天使用的代码示例,原因很简单,输入重定向(<文件构造)到 while 循环造成的“被烧毁”问题,我不再这样做了!在下面的“背景故事”中,你可能会看到这个想法是如何开始的,但它可能都是基于对 Bash 的误解。

简而言之,观察到在读取处理期间在循环中设置的变量(例如本例中的found_target)在循环退出时丢失。一个应该是 Bash 专家的人(早在 1995 年到 97 年)告诉我们——我领导的团队——这是因为循环的内部被放入了一个子壳中。我是一个数据库人,做过机器语言编码,等等,等等,甚至不认为 Bash 是一种编程语言。因此,鉴于交给我的问题需要解决,我只是将一个阅读线程序交还给团队,使他们能够克服困难。

我的简单程序只有一个或两个参数;你告诉它你想要的整数行号,然后通过传递文件或通过文件规范将其指向文件。由于操作系统缓存,它并不像人们想象的那么低效。而且,它让这些技能更差的程序员继续他们的工作。stdin

这个程序非常令人满意,特别是对于大文件——文件越大,胜利就越大,因为 Bash 在这种使用中不是(或至少不是)特别有效,更不用说子壳/全局变量问题了。(请注意以下部分,了解为什么这有意义!

然而,现在我想重新审视这个问题,原因有两个:1)Bash 及其附带的实用程序在过去二十多年中已经/已经取得了长足的进步,并且;2)我想在不依赖我的程序的情况下为某人提供一些软件,对于这个问题,subshell问题是真正的问题 - 并且将使用它的人,就像我为之编写readline的原始人一样,而不是真正的程序员。但是,如果有我的开源版本,那就可以了!readlinereadline

除了这些动机之外,虽然从那时起我在理解 Bash 方面取得了长足的进步,但对我来说,它仍然是一个主要的第三级问题,我知道我仍然对它的大部分内容一无所知。我认为“正确的方法”可能是更智能地使用功能。那时,我对重定向到 BASH 函数和从 BASH 函数重定向的能力一无所知。而且,坦率地说,虽然现在我知道“这是一个东西”,但我从未真正使用过它。

一些背景故事

对于“逆向计算”社区来说,这绝对是这样的事情:早在 1995 年或 1996 年,当这个“不要那样做!”的想法出现时,Bash 被用作“胶水层”的一部分,试图连接大约 7 个系统,这些系统由不同的团队为地球科学的不同方面设计。这些系统都不是设计得那么好,都是由地球科学家完成的,他们热衷于地球,而不是计算机。对于大多数人来说,他们对数据库的想法是粗糙的,通常是大文件中的大文本行,他们想要的只是在当时被认为是巨大的文件中间挑选几行可能相邻的行。而且,要把大气数据与海面数据结合起来,最好的办法是让一些研究生或博士后编写 Bash 代码,将其他代码的一点点组合在一起。

值得一提的是,我的目标是让他们使用关系数据库引擎,事实上,现代PostgreSQL同时来自同一个实验室。然而,我管理的最好的办法是使用数据库作为元层,知道哪些数据在哪些系统中,如何访问这些系统,以及调用哪些程序来实际执行数据连接的科学部分。希望这个题外话能给它一些视角来解释为什么!

嘿,如果整个子壳问题完全是错误的,请教我!我可以被教导!否则,关于替换我的读行的建议会很好。

bash readline 子外壳

评论

1赞 Cyrus 8/6/2023
添加到您的循环中,然后在您的循环之后?x="$line"echo "$x"
1赞 Mikael Öhman 8/6/2023
我不确定我是否理解这里的目标,但是或或会从文件中获取特定行。awk 'NR==123' file.txthead -n 123 file.txt | tail -n 1sed -n '123p' file.txt'
0赞 Gordon Davisson 8/6/2023
我对这个问题以及为什么要解决它感到非常困惑。您在问题中给出的循环不会在子 shell 中运行任何内容,因此它不会有任何与之相关的问题。如果使用 ,则管道将使循环在子 shell 中运行,但对于普通重定向不会发生这种情况(有关详细信息和解决方法,请参阅 BashFAQ #24)。readlinewhile read ... done < some_filesomecommand | while read ...
1赞 Gordon Davisson 8/6/2023
此外,如果您实际上是逐行处理文件,则循环将比使用外部程序更快,尤其是对于大文件。这是因为要获得第 1000 行,程序必须读取并丢弃前 999 行;然后,要获得第 1001 行,它必须读取这 999 行 #1000,然后再次丢弃它们;等。这提供了二次性能:文件大小增加一倍,处理所有行所需的时间增加四倍。除此之外,运行外部命令涉及创建一个进程,而该进程本身就很慢。while read
1赞 Charles Duffy 8/6/2023
是的,bash 很慢(只要它一次调用一个字符的 syscall)——但是为你想读取的每一行运行一个新的 or 或任何其他工具甚至更慢。请提出一个具体的实际问题,以便我们可以深入研究特定现实世界场景的性能,而不是任何高级问题,以至于任何答案本质上都是推测性的。readreadsedawk

答: 暂无答案