起因是这样的,在一个常规的日志处理脚本中,最普通不过的while read line;do XXXX;done<file的应用场景。可是发现文件处理完后,该脚本并没有停止,仍在不停执行,准确点说,是死循环了。第一反应是想到是不是文件格式问题,导致在判断文件结束上出现了问题?但所有的文件都是在服务器上直接生成或创建的,不会存在这个问题。脚本通读了几遍,未果;无奈之下,只好祭出bash -x来。才发现,原来是在敲脚本时,不知怎么手抖了一下,在while和do语句之间,打上了个echo语句。这个就是罪魁祸首了,删掉后,脚本就恢复正常了。

如果就这么过去了,多没意思,我觉得有必要深究一下while的运行机制。

假设while_test.sh脚本内容如下:

#!/bin/bash  
  
let sum=0
while((sum<10))  
do  
echo $sum  
let sum++  
done  

运行后,很正常,在屏幕上打印:
0
1
2
3
4
5
6
7
8
9

下面对脚本进行下修改,在while和do之间加上一个echo语句:

#!/bin/bash  
  
let sum=0  
while((sum<10))  
echo "nima"  
do  
echo $sum  
let sum++  
done  

运行之,死循环如约而至,屏幕上翻滚着:

…..(略)……
nima
1779
nima
1780
nima
1781
nima
1782
nima
1783
nima
1784
nima
1785
nima
1786
nima
1787
nima
1788
……(略)……

到这里,答案已经浮出水面了,问题不在bash,而在于受C语言的影响,我们习惯性的认为while应该把sum<10作为循环进行的条件。但bash里,碰到while后,它会把while到do之间的所有语句的执行结果作为循环进行的条件,而所谓“所有语句的执行结果”,就是这所有语句中,最后一个语句的执行结果。如果这最后一个语句执行成功,则继续执行do到done之间的内容,循环继续;如果执行失败,则退出循环。按照shell的惯例,执行成功与否,可以根据返回码来判断,返回码为0,则成功,非0则失败。

验证一下,将脚本改为如下:

#!/bin/bash  
  
let sum=0  
while((sum<5))  
echo "nima"  
echo "niba"  
[ $sum -le 10 ]  
do  
echo $sum  
let sum++  
done  

执行之,在屏幕上打印:

nima
niba
0
nima
niba
1
nima
niba
2
nima
niba
3
nima
niba
4
nima
niba
5
nima
niba
6
nima
niba
7
nima
niba
8
nima
niba
9
nima
niba
10
nima
niba

当在循环中,sum增加到10后,再次执行while后面的语句,打印nima和niba,但执行[ $sum -le 10 ]时,返回码为非0.所以循环退出。而((sum<5)),完全是个摆设。