我習慣了while 循環中的bash
內建函數,例如:read
echo "0 1
1 1
1 2
2 3" |\
while read A B; do
echo $A + $B | bc;
done
我一直在從事一些make
項目,分割文件和儲存中間結果變得謹慎。因此,我經常最終將單行分解為變數。雖然下面的例子效果很好,
head -n1 somefile | while read A B C D E FOO; do [... use vars here ...]; done
這有點愚蠢,因為 while 循環永遠不會運行多次。但如果沒有while
,
head -n1 somefile | read A B C D E FOO; [... use vars here ...]
當我使用它們時,讀取的變數總是空的。我從未註意到 的這種行為read
,因為通常我會使用 while 迴圈來處理許多類似的行。如何在沒有 while 迴圈的情況下使用bash
's內建函數?read
或者是否有另一種(甚至更好)的方法將一行讀入多個(!)變數?
結論
答案告訴我們,這是一個範圍界定的問題。該聲明
cmd0; cmd1; cmd2 | cmd3; cmd4
被解釋為命令cmd0
、cmd1
和cmd4
在相同的作用域中執行,而命令cmd2
和cmd3
每個都被賦予自己的子 shell,因此具有不同的作用域。原始 shell 是兩個子 shell 的父 shell。
答案1
這是因為使用變數的部分是一組新的命令。使用這個代替:
head somefile | { read A B C D E FOO; echo $A $B $C $D $E $FOO; }
請注意,在此語法中,在 後面必須有一個空格,在 之前必須{
有一個(分號) 。也沒有必要;只讀取第一行。;
}
-n1
read
為了更好地理解,這可能會對您有所幫助;它的作用與上面相同:
read A B C D E FOO < <(head somefile); echo $A $B $C $D $E $FOO
編輯:
人們常說接下來的兩個語句具有相同的作用:
head somefile | read A B C D E FOO
read A B C D E FOO < <(head somefile)
嗯,不完全是。第一個是 from head
tobash
的內建管道read
。一個行程的標準輸出到另一個行程的標準輸入。
第二個語句是重定向和進程替換。它是由bash
它自己處理的。它會建立一個 FIFO(命名管道<(...)
) ,head
其輸出連接到該 FIFO,並將<
其重定向 () 到read
進程。
到目前為止,這些看起來是等價的。但當使用變數時,這可能很重要。在第一個中,執行後未設定變數。在第二個中,它們在當前環境中可用。
在這種情況下,每個 shell 都有不同的行為。看那個連結他們是為了這個。您可以使用命令分組、進程替換 ( ) 或此處字串 ( )bash
來解決該行為。{}
< <()
<<<
答案2
引用一篇非常有用的文章wiki.bash-hackers.org:
這是因為管道的指令在無法修改父 shell 的子 shell 中執行。因此,父 shell 的變數不會被修改(請參閱文章:Bash 和進程樹)。
由於答案已經提供了幾次,另一種方法(使用非內建命令...)是這樣的:
$ eval `echo 0 1 | awk '{print "A="$1";B="$2}'`;echo $B $A
$ 1 0
答案3
正如您所指出的,問題在於管道read
在子 shell 中運行。
一個答案是使用特雷多克:
numbers="01 02"
read first second <<INPUT
$numbers
INPUT
echo $first
echo $second
這個方法很好,因為它在任何類似 POSIX 的 shell 中都會以相同的方式運作。