脚本和下标之间的Stdout竞争条件

时间:2014-09-21 10:01:41

标签: shell stdout zsh race-condition io-redirection

我尝试调用脚本 deepScript 并在另一个脚本 shallowScript 中处理其输出;它看起来像下面的代码:

shallowScript.sh

#!/bin/zsh
exec 1> >( tr "[a-z]" "[A-Z]" )
print "Hello - this is shallowScript"
. ./deepScript.sh

deepScript.sh

#!/bin/zsh
print "Hello - this is deepScript"

现在,当我运行 ./ shallowScript.sh 时,结果是不稳定的:要么按预期工作(很少),要么打印一个空行后跟两个预期行(有时候),或者它打印两行,然后挂起,直到我点击返回并给它换行(大部分时间)。 到目前为止,我发现了以下内容:

  • 这可能是竞争条件,因为两个" print" s尝试同时输出到stdout;插入"睡眠1"在致电"之前。 ./deepScript.sh"一致地纠正问题
  • 问题来自过程替换" exec 1> >(tr ...)&#34 ;;评论它也能一致地纠正问题

我浏览了很多关于进程替换和重定向的论坛和帖子,但无法找到如何保证我的脚本同步调用命令。想法?

zsh --version                                                                                                                                                                   
zsh 5.0.5 (x86_64-apple-darwin14.0)

[编辑]

由于此策略似乎必然会失败或导致可怕的解决方法语法,因此这是另一种似乎与可承受语法一起使用的策略:我从 shallowScript.sh 中删除了所有重定向创建了第三个脚本,输出处理在函数中发生:

shallowScript.sh

#!/bin/zsh
print "Hello - this is shallowScript"
. ./deepScript.sh

thirdScript.sh

#!/bin/zsh
function _process {
  while read input; do
    echo $input | tr "[a-z]" "[A-Z]"
  done
}
. ./shallowScript.sh | _process

1 个答案:

答案 0 :(得分:1)

我想问题是你在执行脚本后没有看到提示符:

$ ./shallowScript.sh
$ HELLO - THIS IS SHALLOWSCRIPT
HELLO - THIS IS DEEPSCRIPT
(nothing here)

并认为它挂在这里并等待换行符。实际上它没有,行为是非常期待的。

您可以输入任何shell命令,而不是换行符,例如ls它将被执行。

$ ./shallowScript.sh
$ HELLO - THIS IS SHALLOWSCRIPT  <--- note the prompt in this line
HELLO - THIS IS DEEPSCRIPT
echo test                        <--- my input
test                             <--- its result
$

这里发生的是:第一个shell(正在运行的shallowScript.sh)创建一个管道,执行dup2调用以将其stdout(fd 1)转发到写入创建管道的结尾然后分叉一个新进程(tr),以便父进程打印到stdout的所有内容都发送到stdin的{​​{1}}。

接下来发生的是主shell(您键入初始命令tr的那个)并不知道它应该延迟打印下一个命令提示符,直到./shallowScript.sh进程结束。它对tr一无所知,所以只等待tr执行,然后打印提示。 shallowScript.sh当时仍然在运行,这就是为什么它的输出(两行)在打印提示后出现,并且您认为shell正在等待换行。事实上,它已经为下一个命令做好了准备。您可以在脚本输出之前,之内或之后的某处看到打印的提示符(tr字符或其他内容),这取决于$进程完成的速度。

每次你的进程分叉时,你会看到这种行为,当父母已经死亡时,孩子继续写入tr

长话短说,试试这个:

stdout

这里shell会在打印下一个提示之前等待$ ./shallowScript.sh | cat HELLO - THIS IS SHALLOWSCRIPT HELLO - THIS IS DEEPSCRIPT $ 进程完成,cat只有在所有输入(例如cat的输出)都是完成时才会完成处理,就像你期望的那样。

更新:在zsh docs中找到相关引文:http://zsh.sourceforge.net/Doc/Release/Expansion.html#Process-Substitution

  

tr还有一个问题;当它附加到外部命令时,父shell不会等待进程完成,因此紧接着的命令不能依赖于结果完成。问题和解决方案与重定向中的>(process)部分中描述的相同。因此,在上面的例子的简化版本中:

MULTIOS
     

(请注意,不涉及paste <(cut -f1 file1) <(cut -f3 file2) > >(process) ),就父shell而言,进程将异步运行。解决方法是:

MULTIOS

在你的情况下它会给出这样的东西:

{ paste <(cut -f1 file1) <(cut -f3 file2) } > >(process)

当然有效,但看起来比原版更糟糕。