Shell脚本中,while循环使用ssh的坑(使用readline、ssh)

一、第一个坑

最近在写一个脚本,读取一个IP文件,遍历ssh后执行一些操作。但是很奇怪,永远在连上第一个IP以后,循环就结束了,不会对下面的IP进行遍历。

问题代码:

# $1是一个主机列表
cat $1 | while read LINE
do
	echo "---------$LINE--------"
	ssh $LINE mv /tmp/test.txt /opt/
done

出现问题:

永远只会连接第一台机器,进行移动操作。列表中其余主机被略过。

问题原因:

while中使用重定向机制,$1 文件中的全部信息都已经读入并重定向给了while语句。所以当我们在while循环中再一次调用read语句,就会读取到下一条记录。问题就出在这里,ssh语句正好会读取输入中的所有东西,下面这个例子就能说明这个问题:

cat $1|while read LINE
do
	echo "-------------$LINE---------"
	ssh 192.168.0.6 cat
done

上面代码运行结果:

运行结果

通过示例可以发现,ssh中的cat语句会打印出 $1 文件中的其他纪录,这就导致调用完ssh语句后,输入缓存中已经都被读完了,当read语句再读的时候当然也就读不到纪录,循环也就退出了。

如何解决?

1. 将ssh的输入重定向

cat $1|while read LINE
do
	echo "-------------$LINE---------"
	ssh 192.168.0.6 ls < /dev/null
done

2. 使用for循环实现

for LINE in `cat $1`
do
	echo "-------------$LINE---------"
	ssh 192.168.0.6 cat
done

3. 若坚持使用while循环,那么需要对ssh增加-n参数

为什么增加了-n参数也可以解决问题呢?通过man ssh查看-n参数的说明:
Redirects stdin from /dev/null (actually, prevents reading from stdin)

修改后的代码如下:

for LINE in `cat $1`
do
	echo "-------------$LINE---------"
	ssh -n 192.168.0.6 cat
done

  

  

参考自:https://hywelzhang.github.io/2017/09/06/Linux-Shell-Shell%E4%B8%ADwhile%E5%BE%AA%E7%8E%AF%E4%BD%BF%E7%94%A8ssh%E8%B8%A9%E5%9D%91%E5%A4%87%E6%B3%A8.html

http://www.yunweipai.com/archives/4339.html

 

 

二、第二个坑

 

起因是这样的,在一个常规的日志处理脚本中,最普通不过的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

运行后,很正常,在屏幕上打印

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

#!/bin/bash

let sum=0
while((sum<10))
echo "nima"
do
echo $sum
let sum++
done

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

#!/bin/bash

let sum=0
while((sum<10))
echo "nima"
do
echo $sum
let sum++
done

到这里,答案已经浮出水面了,问题不在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

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

 参考:http://www.pureage.info/2013/08/22/122.html

posted @ 2017-09-13 14:57  夏覓  Views(4046)  Comments(0Edit  收藏  举报