2011-05-16 12:14:48 +0000 2011-05-16 12:14:48 +0000
247
247

Schlagen: Iterieren über Zeilen in einer Variable

Wie iteriert man korrekt über Zeilen in bash entweder in einer Variable oder von der Ausgabe eines Befehls aus? Einfach die IFS-Variable auf eine neue Zeile zu setzen, funktioniert bei der Ausgabe eines Befehls, aber nicht bei der Verarbeitung einer Variablen, die neue Zeilen enthält.

Zum Beispiel

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

Dies ergibt die Ausgabe:

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

Wie Sie sehen können, wird durch das Echo auf die Variable oder die Iteration über den Befehl cat jede der Zeilen eine nach der anderen korrekt ausgegeben. Die erste für Schleife druckt jedoch alle Posten in einer einzigen Zeile. Haben Sie eine Idee?

Antworten (5)

312
312
312
2011-05-16 13:47:36 +0000

Wenn Sie mit bash Zeilenumbrüche in eine Zeichenkette einbetten möchten, umschließen Sie die Zeichenkette mit $'':

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

Und wenn Sie eine solche Zeichenkette bereits in einer Variablen haben, können Sie sie Zeile für Zeile lesen mit:

while IFS= read -r line; do
    echo "... $line ..."
done <<< "$list"
72
72
72
2011-05-16 12:21:28 +0000

Sie können while + read verwenden:

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

Übrigens ist die Option -e bis echo kein Standard. Verwenden Sie stattdessen printf, wenn Sie Portabilität wünschen.

32
32
32
2014-03-17 21:45:33 +0000
#!/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
15
15
15
2011-05-16 12:45:14 +0000

Hier ist eine lustige Art und Weise, Ihre for-Schleife zu machen:

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

Etwas sinnvoller/lesbarer wäre:

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

Aber das ist alles zu komplex, Sie brauchen dort nur ein Leerzeichen:

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

Ihre Variable $line enthält keine Zeilenumbrüche. Sie enthält Instanzen von `Hier ist eine lustige Art und Weise, Ihre for-Schleife zu machen:

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

Etwas sinnvoller/lesbarer wäre:

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

Aber das ist alles zu komplex, Sie brauchen dort nur ein Leerzeichen:

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

Ihre Variable $line enthält keine Zeilenumbrüche. Sie enthält Instanzen von gefolgt vonn. Das können Sie deutlich sehen mit:

$ 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

Die Ersetzung ersetzt diese durch Leerzeichen, was für Schleifen ausreicht:

$ 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

Demo:

$ 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
3
3
3
2019-05-07 16:19:00 +0000

Sie können die Variable auch zuerst in ein Array konvertieren und dann darüber iterieren.

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

Dies ist nur sinnvoll, wenn Sie sich nicht mit dem Befehl IFS herumschlagen wollen und auch Probleme mit dem Befehl read haben, da es passieren kann, wenn Sie ein anderes Skript innerhalb der Schleife aufrufen, dass dieses Skript Ihren Lesepuffer leeren kann, bevor es zurückkehrt, wie es mir passiert ist.