題 Bash:迭代變量中的行


如何在變量或命令輸出中正確迭代bash中的行?只需將IFS變量設置為新行即可用於輸出命令,但在處理包含新行的變量時則無效。

例如

#!/bin/bash

list="One\ntwo\nthree\nfour"

#Print the list with echo
echo -e "echo: \n$list"

#Set the field separator to new line
IFS=$'\n'

#Try to iterate over each line
echo "For loop:"
for item in $list
do
        echo "Item: $item"
done

#Output the variable to a file
echo -e $list > list.txt

#Try to iterate over each line from the cat command
echo "For loop over command output:"
for item in `cat list.txt`
do
        echo "Item: $item"
done

這給出了輸出:

echo: 
One
two
three
four
For loop:
Item: One\ntwo\nthree\nfour
For loop over command output:
Item: One
Item: two
Item: three
Item: four

正如您所看到的,回顯變量或迭代變量 cat 命令一個一個地正確打印每一行。但是,第一個for循環在一行上打印所有項目。有任何想法嗎?


176
2018-05-16 12:14


起源




答案:


使用bash,如果要在字符串中嵌入換行符,請用字符串括起來 $''

$ list="One\ntwo\nthree\nfour"
$ echo "$list"
One\ntwo\nthree\nfour
$ list=$'One\ntwo\nthree\nfour'
$ echo "$list"
One
two
three
four

如果你已經在一個變量中有這樣一個字符串,你可以逐行讀取它:

while read -r line; do
    echo "... $line ..."
done <<< "$list"

226
2018-05-16 13:47



只是注意到了 done <<< "$list" 至關重要 - Jason Axelson
原因 done <<< "$list" 至關重要的是因為它會傳遞“$ list”作為輸入 read - wisbucky
我需要強調一下:雙引號 $list 非常關鍵。 - André Chalella
我怎麼用灰燼做的呢?因為我得到了 syntax error: unexpected redirection。 - atripes
echo "$list" | while ... 看起來比...更清楚 ... done <<< "$line" - kyb


您可以使用 while + read

some_command | while read line ; do
   echo === $line ===
done

順便說一句。該 -e 選項 echo 是非標準的。使用 printf 相反,如果你想要便攜性。


51
2018-05-16 12:21



請注意,如果使用此語法,循環內分配的變量將不會在循環後粘住。奇怪的是, <<< glenn jackman建議的版本 不 使用變量賦值。 - Sparhawk
@Sparhawk是的,那是因為管道開始執行子shell while 部分。該 <<< 版本沒有(至少在新的bash版本中)。 - maxelost


#!/bin/sh

items="
one two three four
hello world
this should work just fine
"

IFS='
'
count=0
for item in $items
do
  count=$((count+1))
  echo $count $item
done

21
2018-03-17 21:45



這會輸出所有項目,而不是行。 - Tomasz Posłuszny
@TomaszPosłuszny不,這在我的機器上輸出3(計數是3)行。請注意IFS的設置。你也可以使用 IFS=$'\n' 為了同樣的效果。 - jmiserez


這是一個有趣的方式來做你的for循環:

for item in ${list//\\n/
}
do
   echo "Item: $item"
done

更合理/可讀的是:

cr='
'
for item in ${list//\\n/$cr}
do
   echo "Item: $item"
done

但這太複雜了,你只需要一個空間:

for item in ${list//\\n/ }
do
   echo "Item: $item"
done

$line 變量不包含換行符。它包含的實例 \ 其次是 n。你可以清楚地看到:

$ cat t.sh
#! /bin/bash
list="One\ntwo\nthree\nfour"
echo $list | hexdump -C

$ ./t.sh
00000000  4f 6e 65 5c 6e 74 77 6f  5c 6e 74 68 72 65 65 5c  |One\ntwo\nthree\|
00000010  6e 66 6f 75 72 0a                                 |nfour.|
00000016

替換是用空格替換,這足以使它在for循環中工作:

$ cat t.sh
#! /bin/bash
list="One\ntwo\nthree\nfour"
echo ${list//\\n/ } | hexdump -C

$ ./t.sh 
00000000  4f 6e 65 20 74 77 6f 20  74 68 72 65 65 20 66 6f  |One two three fo|
00000010  75 72 0a                                          |ur.|
00000013

演示:

$ cat t.sh
#! /bin/bash
list="One\ntwo\nthree\nfour"
echo ${list//\\n/ } | hexdump -C
for item in ${list//\\n/ } ; do
    echo $item
done

$ ./t.sh 
00000000  4f 6e 65 20 74 77 6f 20  74 68 72 65 65 20 66 6f  |One two three fo|
00000010  75 72 0a                                          |ur.|
00000013
One
two
three
four

7
2018-05-16 12:45



有意思,你能解釋一下這裡發生了什麼嗎?看起來你正在用新行替換\ n ...原始字符串和新字符串之間有什麼區別? - Alex Spurling
@Alex:更新了我的答案 - 用更簡單的版本:-) - Mat
我嘗試了這個,如果輸入中有空格,它似乎不起作用。在上面的例子中,我們有“一個\ ntwo \ nthree”,但是如果我們有“entry one \ nentry two \ nentry three”它會失敗,因為它也為空間添加了一個新行。 - John Rocha
只是注意而不是定義 cr 您可以使用 $'\n'。 - devios1