脚本和下标之间的标准输出竞争条件 [英] Stdout race condition between script and subscript

查看:224
本文介绍了脚本和下标之间的标准输出竞争条件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to call a script deepScript and process its output within another script shallowScript ; it looks schematically like the following pieces of code:

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 时,结果是不稳定的:要么按预期运行(很少),要么先打印一个空行,然后打印两个预期行(有时),否则它会打印两行,然后挂起,直到我按回车键并换行(大多数情况下). 到目前为止,我发现了以下内容:

Now, when I run ./shallowScript.sh, the outcome is erratic : either it works as expected (very rarely), or it prints an empty line followed by the two expected lines (sometimes), or it prints the two lines and then hangs until I hit return and give it a newline (most of the time). So far, I found out the following:

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

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

I've browsed so many forums and posts about process substitution and redirection, but could not find out how to guarantee that my script calls commands synchronously. Ideas ?

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

该策略似乎注定要失败或导致可怕的解决方法语法,这是另一种似乎可以使用可忍受语法的策略:我从 shallowScript.sh 中删除了所有重定向,然后创建了第三个脚本,其中在函数中进行输出处理:

As it seems that this strategy is bound to fail or lead to horrible workaround syntax, here is another strategy that seems to work with a bearable syntax: I removed all the redirect from shallowScript.sh and created a third script where the output processing happens in a function:

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

推荐答案

我想问题是执行脚本后您看不到提示:

I suppose the problem is that you don't see the prompt after executing the script:

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

,并认为它挂在这里并等待换行符.实际上,事实并非如此,并且这种行为是非常值得期待的.

and think it hangs here and waits for the newline. Actually it does not, and the behavior is very expected.

您可以输入任何shell命令来代替换行符,例如ls,它将被执行.

Instead of the newline you can enter any shell command e.g. ls and it will be executed.

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

这里发生的是:第一个外壳程序(正在运行shallowScript.sh的外壳程序)创建管道,执行dup2调用以将其stdout(fd 1)转发到创建的管道的写入端,然后然后派生一个新进程(tr),以便将父级打印到stdout的所有内容发送到trstdin.

What happens here is: the first shell (the one which is running shallowScript.sh) creates a pipe, executes a dup2 call to forward its stdout (fd 1) to the write end of the created pipe and then forks a new process (tr) so that everything the parent prints to stdout is sent to the stdin of tr.

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

What happens next is that the main shell (the one where you type the initial command ./shallowScript.sh) does not have an idea that it should delay printing the next command prompt until the end of tr process. It knows nothing about tr, so it just waits for the shallowScript.sh to execute, then prints a prompt. The tr is still running at that time, that's why its output (two lines) come after the prompt is printed, and you think the shell is waiting for the newline. It is not actually, it is ready for the next command. You can see the printed prompt ($ character or whatever) somewhere before, inside, or after the output of the script, it depends on how fast the tr process finished.

每次您的进程派生时,您都会看到这种行为,并且当父进程已死时,子进程继续写入其stdout.

You see such behavior every time your process forks and the child continues to write to its stdout when the parent is already dead.

长话短说,试试这个:

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

在这里,外壳程序将在打印下一个提示之前等待cat进程完成,并且cat仅在处理完其所有输入(例如,来自tr的输出)后才会完成,就像您期望的那样

Here the shell will wait for the cat process to finish before printing a next prompt, and the cat will finish only when all its input (e.g. the output from tr) is processed, just as you expect.

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

Update: found a relevant quote in zsh docs here: http://zsh.sourceforge.net/Doc/Release/Expansion.html#Process-Substitution

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

There is an additional problem with >(process); when this is attached to an external command, the parent shell does not wait for process to finish and hence an immediately following command cannot rely on the results being complete. The problem and solution are the same as described in the section MULTIOS in Redirection. Hence in a simplified version of the example above:

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

(请注意,不涉及任何MULTIOS),就父外壳而言,进程将异步运行.解决方法是:

(note that no MULTIOS are involved), process will be run asynchronously as far as the parent shell is concerned. The workaround is:

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

在您的情况下,它将给出以下内容:

In your case it will give something like this:

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

当然可以,但是看起来比原始版本差.

which of course works but looks worse than the original.

这篇关于脚本和下标之间的标准输出竞争条件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆