發現這個問題來自對SGE的startmpi.sh指令碼做改造的時候。考慮如下一段shell代碼:
#!/bin/sh
# declare -i i
testline="hello:"
cat testfile | while read line; do
testline="${testline}${line} "
echo "In cycle, the testline is: $testline"
# for (( i=0; i<3; i++ )) do
# testline="${testline}${i} "
#i=0
#while [ $i -lt 3 ]; do
# testline="${testline}${i} "
# i=$i+1
done
echo $testline
代碼中用到的testfile可以是任何文本的檔案,比如:
phy2 2
phy3 2
phy4 2
這樣的情況下,代碼啟動並執行結果出乎我的意料,testline這個變數在while迴圈中就好像沒有被賦過值一樣,輸出是這樣的:
In cycle, the testline is: hello:phy2 2
In cycle, the testline is: hello:phy2 2 phy3 2
In cycle, the testline is: hello:phy2 2 phy3 2 phy4 2
hello:
這是為什麼呢?參考上面的代碼,如果把cat testfile | while read line; do這一段注釋掉,開啟for迴圈那一段,或開啟while迴圈那一段的話,testline就都能在迴圈中被賦值,最後的echo $testline就是正確的。這是為什麼呢?
最後發現是管道符|引發的問題。原因非常簡單,之前在學習shell的時候就學過shell執行命令的原理,裡面也有對管道的描述。簡單來說,在shell中使用管道符號,會產生subshell,從而使在這個subshell中對變數的賦值只在subshell中生效,不會影響到shell指令碼本身的進程。
但是ksh現在有個feature,能使管道中對變數的賦值反映到父shell中來,這就是網上有人問說這樣的代碼在k shell中就是OK的原因。
附上網上搜來的一篇文章,講解的不錯。
/Files/super119/bash_pipe_variable_lost.mht.zip