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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM