我正试图调用一个脚本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
的所有内容都会发送到tr
的stdin
。
接下来发生的情况是,主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]" )
这当然有效,但看起来比原版更糟糕。