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



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

浅层Script.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"

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

  • 这可能是一个竞争条件,因为两个"print"试图同时输出到stdout;在调用"../deepScript.sh"之前插入"sleep 1"可以始终如一地纠正问题
  • 问题来自于进程替换"exec 1>(tr…)";评论出来也能始终如一地纠正问题

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

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

[EDIT]

由于这种策略似乎注定会失败或导致糟糕的变通语法,这里有另一种策略似乎适用于可以忍受的语法:我删除了shallScript.sh中的所有重定向,并创建了第三个脚本,其中输出处理发生在函数中:

浅层Script.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

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

$ ./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的shell)创建一个管道,执行dup2调用将其stdout(fd 1)转发到创建的管道的写入端,然后分叉一个新进程(tr),这样父进程打印到stdout的所有内容都会发送到trstdin

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

每次进程分叉时,您都会看到这样的行为,并且当父进程已经死时,子进程继续向其stdout写入。

长话短说,试试这个:

$ ./shallowScript.sh | cat
HELLO - THIS IS SHALLOWSCRIPT
HELLO - THIS IS DEEPSCRIPT
$

在这里,shell将等待cat进程完成,然后再打印下一个提示,而cat只有在其所有输入(例如tr的输出)都得到处理时才会完成,正如您所期望的那样。

更新:在zsh文档中找到了相关报价:http://zsh.sourceforge.net/Doc/Release/Expansion.html#Process-替代

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

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

(注意,不涉及MULTIOS),就父shell而言,进程将异步运行。解决方法是:

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

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

{
        print "Hello - this is shallowScript"
        . ./deepScript.sh
} 1> >( tr "[a-z]" "[A-Z]" )

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

最新更新