Bash while 循环意外停止

Bash while loop stops unexpectedly

我正在分析两个具有我不理解的行为的脚本:

#/bin/bash
tijd=${1-60}
oud=`ls -l $MAIL`
while : ; do
   nieuw=`ls -l $MAIL`
   echo $oud $nieuw
   sleep $tijd
done | { read a b rest ; echo $a ; echo $b ; echo $rest ; }

此脚本中的 while 循环在一次迭代后停止。

#/bin/bash
tijd=${1-60}
oud=`ls -l $MAIL`
while : ; do
   nieuw=`ls -l $MAIL`
   echo $oud $nieuw
   sleep $tijd
done | cat

此脚本中的 while 循环是无限的。

有什么区别?我认为这是管道和支架的问题,但我无法解释。

问题是{read a b rest; ..}块只执行一次,并且读取命令读取一行输入,而您希望它阅读很多行。

你想写的是这样的:

#/bin/bash
tijd=${1-60}
oud=`ls -l $MAIL`
while read a b rest; do
  echo $a
  echo $b
  echo $rest
done < <(
  while : ; do
    nieuw=`ls -l $MAIL`
    echo $oud $nieuw
    sleep $tijd
  done
)

这是一种更 'standard' 的方式。它还避免使用 pipe |,它强制 {read ..} 块作为单独的进程到 运行,然后使得无法观察 主 shell.

中读取命令的效果

<( .. ) 称为 进程替换 。它基本上执行包含的块,同时将其输出捕获到一个临时文件中,然后 returns 该临时文件名。

您在 pipe 之后使用 read 的循环在第一次迭代后终止,因为 SIGPIPE 信号的调用发生在 LHS pipe 写入一个管道,其输出未被读取,因为在 RHS 上 read 周围没有 while 循环)。你的 cat 示例不会退出,因为 cat 连续 从输入中读取 read 终止 阅读一行后。

要了解此行为,请先减少您的示例:

while : ; do pwd; done | { read -r line; echo $line; }
/Users/admin

所以 read 在第一行之后终止。要验证此启用 pipefail 使用:

set -o pipefail

并检查退出状态:

while : ; do pwd; done | { read -r line; echo "$line"; }
echo $?
141

存在状态 141 是由于 SIGPIPE


要解决此问题,请在 while 循环内更改管道 RHS 上的 read

while : ; do pwd; sleep 5; done | { while read -r line; do echo "$line"; done; }
/Users/admin
/Users/admin
/Users/admin

现在您根本看不到命令​​退出,因为 while read 不断从管道的 LHS 捕获所有数据。