2011-12-09 120 views
3

好吧,所以我試圖解析一個文件,輸出列中我需要的行,我不能讓這個while循環工作,我真的很難過。爲什麼這個For循環與Bash中的While循環不一樣?

有趣的是,使用for循環做幾乎完全相同的事情確實工作。有人能解釋一下這裏發生了什麼嗎?

這...

e="" 
for f in 1 2 3 
do 
    echo $f 
    e="$e.$f" 
done 
echo $e 

輸出:

1 
2 
3 
.1.2.3 

但這...

e="" 
echo "1 
2 
3" | while read f 
do 
    echo $f 
    e="$e.$f" 
done 
echo $e 

輸出:

1 
2 
3 

顯然,當他們到達e="$e.$f"時,兩個循環在$f中都有1,2或3,所以第二個不起作用的是什麼?

回答

8

我不能告訴你有多少次我不得不從某人的代碼中提取這個bug。

基本上管道會生成一個分叉過程。 e被設置在分叉進程中,但是當它終止時(在wile循環結束時),父進程不知道e已經改變。總之,從不在涉及管道的命令中設置變量。管道結束時不會設置。更短...如果可以避免使用管道,請勿使用管道。

e='' 
while read f 
do 
    echo $f 
    e="$e.$f" 
done << EOF 
1 
2 
3 
EOF 
echo $e 
+0

太棒了,謝謝!我的第二個問題是「我該如何解決它?「 – Chriszuma

1

我猜這是變量範圍的差異。當您將echo語句放入while語句中時,全局e不在其範圍內,因此它會打印f並設置一個新變量e。當while循環退出時,全局e沒有改變。因此你的陳述echo $e什麼都不打印。

+0

管道有自己的本地範圍? 0_o – Chriszuma

+0

這就好像echo是一個分叉進程並且什麼也不返回。這是一個孩子的過程,並得到一個新的範圍。如果你可以通過e作爲命令的參考,它會更新全局可訪問的e。 – Furbeenator

3

管道將在子外殼中執行。所以變量e在管道命令執行後沒有改變。

+0

有趣,我不知道。 – Chriszuma

+0

是的,即使你在while循環中導出,var也不能被「推送」到父shell。 – Kent

3

由於while read... done是管道的一部分,它得到了自己的分叉過程圖像,就像其他的二進制文件(也就是所謂的一個子shell)。 (令人困惑的部分是它是用shell編寫的,但它不會作爲當前shell進程的一部分執行。)這就是爲什麼變量不會更新的原因。

作爲一種變通方法,您可以送花兒給人做這樣的事情(使用bash化):

while read line 
do 
    blablabla 
done < <(echo "1 2 3") 

在這裏,你避免建立管道,所以while...done shell代碼被作爲當前腳本的一部分執行。

+0

+ +1向我展示了一個新的表示法,我從來沒有見過'<()' – Chriszuma

+0

這被稱爲**過程替換**,並且不是很便攜(但可以通過創建一個臨時文件來模擬) –