2009-11-25 105 views
3

爲什麼FILE_FOUND是0在這個開溜的結尾:Linux shell錯誤?變量賦值管道不起作用

FILE_FOUND=0 

touch /tmp/$$.txt 

ls -1 /tmp/$$.* 2>/dev/null | while read item; do 
    FILE_FOUND=1 
    echo "FILE_FOUND = $FILE_FOUND" 
done 

echo "FILE_FOUND = $FILE_FOUND" 

rm -f /tmp/$$.txt 2>/dev/null 

?? !!

在Unix上FILE_FOUND停留在1(因爲它應該),但在Linux(RedHat,Cygwin,..)它跳回到0!

它是Linux的shell功能,而不是bug? :)

請幫忙。

+2

哪個Linux外殼您使用的?有很多,每個都有自己的「功能」。 – 2009-11-25 13:48:44

+0

Duplicate:http://stackoverflow.com/questions/1789750/global-scope-of-variable – 2009-11-25 14:16:51

+1

請注意,流水線中不需要'ls'的'-1'選項。 – 2009-11-25 14:19:25

回答

5

這是因爲你管道進入while而導致的常見問題,因此它運行在一個子shell中,它不能將環境變量傳遞迴它的父級。我猜「unix」在這方面有所不同,因爲你在那裏運行不同的shell(ksh?)

可能不需要管道到while循環。你能用這個成語嗎?

for item in /tmp/$$.*; do 
    .... 
done 

如果你必須使用一個子shell,那麼你就必須做這樣的處理外在的東西:

touch /tmp/file_found 
+0

我不確定。如果周圍有一個'('...')',那麼你一定是正確的。 – 2009-11-25 13:45:09

+2

http://mywiki.wooledge.org/BashFAQ/024-「這種令人驚訝的行爲的原因是,當它是流水線的一部分時,while/for/until循環在SubShell中運行。」和「當使用重定向或管道使用循環時,不同的shell的行爲會有所不同」 – 2009-11-25 13:48:55

+0

會'export FILE_FOUND = 1' help? – 2009-11-25 13:51:31

3

這是「慶典」殼的「功能」。 「bash」手動輸入清楚地表明:

流水線中的每個命令都作爲一個單獨的進程(即在一個子shell中)執行。

與Korn外殼(「KSH」)執行的相同構建運行「而」在同一進程(未在子shell),並且因此給出了預期的答案。所以我檢查了規範。

POSIX shell specification對此並不十分清楚,但它沒有提到有關更改「shell執行環境」的任何內容,所以我認爲UNIX/Korn shell實現是兼容的,而Bourne Again shell實現不是。但是,「bash」並不聲稱是符合POSIX的!

+0

我花了一段時間才發現差異。 – Amarghosh 2009-11-25 13:47:20

+0

我認爲這是一個錯誤的問題。如果沒有「做」,Bash甚至不會執行該文件。 – falstro 2009-11-25 13:51:48

+0

@roe - 你是對的。我認爲這實在是一個bash「功能」,但我仍在研究。 – 2009-11-25 13:53:40

2

它已經被提及,但是由於你在一段時間內進行管道操作,所以整個while循環都在子shell中運行。我並不確定你在'Unix'上使用了哪個shell,我認爲這意味着Solaris,但bash應該表現出一致性,不管平臺如何。

爲了解決這個問題,你可以做很多事情,最常見的就是以某種方式考試系統while循環的結果,像這樣

result=`mycommand 2>/dev/null | while read item; do echo "FILE_FOUND"; done` 

$result查找數據。另一種常見方法是讓while循環產生有效的變量賦值,並直接對其進行評估。

eval `mycommand | while read item; do echo "FILE_FOUND=1"; done` 

這將由您的'當前'殼評估分配給定的變量。

我假設你不想只是遍歷文件,在這種情況下,你應該做的

for item in /tmp/$$.*; do 
    # whatever you want to do 
done 
-2

Ummmm ...ls -1不是l在您的腳本?

2

正如其他人提到的那樣,這是您使用管道符號創建的額外外殼。試試這個:

while read item; do 
    FILE_FOUND=1 
    echo "FILE_FOUND = $FILE_FOUND" 
done < <(ls -1 /tmp/$$.* 2>/dev/null) 

在這個版本中,while循環是在腳本的外殼,而ls是一個新的shell(一個什麼樣的劇本正在做相反)。

+0

假設用戶的shell能夠理解**進程替換**(可能在linux上) – 2009-11-25 14:18:43

+0

你真的嘗試過嗎?感覺就像bash將while循環放在一個子shell中以使管道正確。 – falstro 2009-11-25 15:08:58

0

另一種方式,把結果返回,

FILE_FOUND=0 
result=$(echo "something" | while read item; do 
    FILE_FOUND=1 
    echo "$FILE_FOUND" 

done) 
echo "FILE_FOUND outside while = $result" 
+0

不需要:你需要*括號內的所有*引用'$ FILE_FOUND' *。 – 2009-11-25 14:18:06