一、第一個坑
最近在寫一個腳本,讀取一個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
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)),完全是個擺設。
