两个块之间的管道操作员



我发现了一个有趣的bash脚本,通过一些修改可能会解决我的用例。但我不确定我是否了解它是如何工作的,尤其是块之间的管道。

这两个块如何协同工作,将它们分开的管道的行为是什么?

function isTomcatUp {
# Use FIFO pipeline to check catalina.out for server startup notification rather than
# ping with an HTTP request. This was recommended by ForgeRock (Zoltan).
FIFO=/tmp/notifytomcatfifo
mkfifo "${FIFO}" || exit 1
{
# run tail in the background so that the shell can
# kill tail when notified that grep has exited
tail -f $CATALINA_HOME/logs/catalina.out &
# remember tail's PID
TAILPID=$!
# wait for notification that grep has exited
read foo <${FIFO}
# grep has exited, time to go
kill "${TAILPID}"
} | {
grep -m 1 "INFO: Server startup"
# notify the first pipeline stage that grep is done
echo >${FIFO}
}
# clean up
rm "${FIFO}"
}

代码来源:https://www.manthanhd.com/2016/01/15/waiting-for-tomcat-to-start-up-in-a-script/

bash有一整套复合命令,其工作原理与简单命令非常相似。这里最相关的是每个复合命令都有自己的标准输入和标准输出。

{ ... }就是这样一种复合命令。组内的每个命令都从组继承其标准输入和输出,因此效果是组的标准输出是其子级标准输出的串联。同样,内部的每个命令依次从组的标准输入中读取。在您的示例中,没有发生任何有趣的事情,因为grep消耗所有标准输入,并且没有其他命令尝试从中读取。但请考虑以下示例:

$ cat tmp.txt
foo
bar
$ { read a; read b; echo "$b then $a"; } < tmp.txt
bar then foo

第一个read从标准输入中获取一行,第二个read从标准输入中获取第二行。重要的是,第一个read在第二个read看到它之前消耗了一行输入。与此形成对比

$ read a < tmp.txt
$ read b < tmp.txt

其中ab都将包含foo,因为每个read命令都会重新打开tmp.txt,并且都将读取第一行。

{ …; }操作对命令进行分组,以便 I/O 重定向应用于其中的所有命令。{必须分开,就好像它是命令名称一样;}前面必须有分号或换行符,并且也是分开的。 这些命令不是在子外壳中执行的,这与( … )不同,后者也有一些语法差异。

在脚本中,有两个通过管道连接的此类分组。 由于管道,每个组都在子壳中,但由于大括号的原因,它不在子壳中。

第一组在后台对文件运行tail -f,然后等待FIFO关闭,以便它可以杀死tail -f。 第二部分查找某些特定信息的第一次出现,当找到它时,停止读取并写入FIFO以释放所有内容。

与任何管道一样,退出状态是最后一个组的状态 — 很可能为 0,因为echo成功。

最新更新