Bash while while循环意外停止

时间:2015-05-24 12:07:38

标签: linux bash while-loop

我正在分析两个脚本,其中包含一些我不理解的行为:

#/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循环是无限的。

有什么区别?我认为它与管道和括号有关,但我无法解释它。

2 个答案:

答案 0 :(得分:3)

管道之后read的循环在第一次迭代后终止,因为调用 SIGPIPE 信号,管道的LHS写入管道输出为不读,因为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的所有数据。

答案 1 :(得分:0)

问题在于 {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
)

这是一个更多的标准&#39;这样做的方式。它还避免使用管道,这会强制 {read ..} 块作为单独的进程运行,这样就无法观察主shell中read命令的效果。

&lt;(..)的事情称为流程替换。它基本上执行包含的块,同时将其输出捕获到临时文件中,然后返回该临时文件名。