使用 bash 的 read 內建函數,無需 while 循環

使用 bash 的 read 內建函數,無需 while 循環

我習慣了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

被解釋為命令cmd0cmd1cmd4在相同的作用域中執行,而命令cmd2cmd3每個都被賦予自己的子 shell,因此具有不同的作用域。原始 shell 是兩個子 shell 的父 shell。

答案1

這是因為使用變數的部分是一組新的命令。使用這個代替:

head somefile | { read A B C D E FOO; echo $A $B $C $D $E $FOO; }

請注意,在此語法中,在 後面必須有一個空格,在 之前必須{ 有一個(分號) 。也沒有必要;只讀取第一行。;}-n1read

為了更好地理解,這可能會對您有所幫助;它的作用與上面相同:

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 headtobash的內建管道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 中都會以相同的方式運作。

相關內容