web-dev-qa-db-ja.com

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コマンドを繰り返すと、各行が1行ずつ正しく印刷されます。ただし、最初のforループはすべての項目を1行に出力します。何か案は?

209
Alex Spurling

Bashでは、改行を文字列に埋め込む場合は、文字列を$''で囲みます。

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

そのような文字列がすでに変数に含まれている場合は、次のように1行ずつ読み込むことができます。

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

while + readを使用できます。

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

ところで。 echoに対する-eオプションは標準的ではありません。移植性が必要な場合は、代わりにprintfを使用してください。

60
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
25

これが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
10
Mat

最初に変数を配列に変換してから、これを反復することもできます。

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

IFSコマンドをめちゃくちゃにしたくない場合や、readコマンドに問題がある場合、ループ内で別のスクリプトを呼び出すと、そのスクリプトが戻ってくる前に読み取りバッファを空にしてしまう可能性があるため私に。

0
Torge