Bash:変数内の行の繰り返し


225

変数またはコマンドの出力のいずれかで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コマンドを反復処理すると、各行が1行ずつ正しく印刷されます。ただし、最初のforループは、すべてのアイテムを1行に出力します。何か案は?


すべての回答に対するコメント:改行文字を削除するには、$(echo "$ line" | sed -e 's / ^ [[:space:]] * //')を実行する必要がありました。
servermanfail

回答:


290

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"

30
これdone <<< "$list"が重要であることに注意してください
ジェイソンアクセルソン

21
その理由done <<< "$list"は、それが「$ list」を入力として渡すためですread
-wisbucky

22
これを強調する必要があり$listます。二重引用符は非常に重要です。
アンドレチャレラ

8
echo "$list" | while ...より明確に見える可能性があります... done <<< "$line"
-kyb

5
whileループはサブシェルで実行されるため、このアプローチには欠点があります。ループで割り当てられた変数は保持されません。
グレンジャックマン

66

while+ を使用できますread

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

ところで への-eオプションechoは非標準です。printf移植性が必要な場合は、代わりに使用してください。


12
この構文を使用すると、ループ内で割り当てられた変数はループの後に固定されないことに注意してください。奇妙なことに、<<<グレン・ジャックマンによって提案されたバージョンがない変数の割り当てで動作します。
スパルホーク

7
@Sparhawkはい、パイプがwhileパーツを実行するサブシェルを開始するためです。<<<バージョンは、(少なくとも、新しいbashのバージョンでは)ありません。
maxelost

26
#!/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

2
これにより、行ではなくすべてのアイテムが出力されます。
トマスポスウシュニ

4
@TomaszPosłusznyいいえ、これは私のマシンで3行(カウントは3)を出力します。IFSの設定に注意してください。IFS=$'\n'同じ効果に使用することもできます。
jmiserez

11

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

興味深いことに、ここで何が起こっているのか説明できますか?\ nを新しい行に置き換えているようです...元の文字列と新しい文字列の違いは何ですか?
アレックススパーリング

@アレックス:私の答えを更新-より簡単なバージョンでも:
マット

私はこれを試してみましたが、入力にスペースが含まれていると機能しないようです。上記の例では「one \ ntwo \ nthree」がありますが、これは機能しますが、「entry one \ nentry two \ nentry three」があると失敗し、スペースにも新しい行が追加されます。
ジョン・ロシャ

2
定義する代わりにcrを使用できることに注意してください$'\n'
devios1

1

最初に変数を配列に変換してから、これを繰り返し処理することもできます。

lines="abc
def
ghi"

declare -a theArray

while read -r line
do
    theArray+=($line)            
done <<< "$lines"

for line in "${theArray[@]}"
do
    echo "$line"
    #Do something complex here that would break your read loop
done

これは、混乱することなくIFSreadコマンドにも問題がある場合にのみ役立ちます。ループ内で別のスクリプトを呼び出して、そのスクリプトが返される前に読み取りバッファを空にすることができます。 。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.