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

Bash: Iteracja nad liniami w zmiennej

Advertisement

Jak prawidłowo iterować nad liniami w bashu albo w zmiennej, albo z wyjścia polecenia? Po prostu ustawienie zmiennej IFS na nową linię działa na wyjście polecenia, ale nie podczas przetwarzania zmiennej zawierającej nowe linie.

Na przykład

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

Daje to wyjście:

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

Jak widzisz, echo zmiennej lub iteracja nad komendą cat wypisuje każdą z linii po kolei poprawnie. Jednak pierwszy dla pętli wypisuje wszystkie elementy w jednej linii. Jakieś pomysły?

Advertisement
Advertisement

Odpowiedzi (5)

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

Z bashem, jeśli chcesz osadzić nowe linie w łańcuchu, dołącz łańcuch z $'':

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

A jeśli masz już taki łańcuch w zmiennej, możesz go odczytać linijka po linijce z:

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

Możesz użyć while + read:

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

Btw. opcja -e do echo jest niestandardowa. Użyj printf zamiast tego, jeśli chcesz mieć przenośność.

32
Advertisement
32
32
2014-03-17 21:45:33 +0000
Advertisement
#!/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

Oto zabawny sposób na zrobienie pętli:

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

Trochę bardziej sensowne/czytelne byłoby:

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

Ale to wszystko jest zbyt skomplikowane, potrzebujesz tylko miejsca:

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

Zmienna $line nie zawiera nowych linii. Zawiera ona instancje `Oto zabawny sposób na zrobienie pętli:

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

Trochę bardziej sensowne/czytelne byłoby:

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

Ale to wszystko jest zbyt skomplikowane, potrzebujesz tylko miejsca:

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

Zmienna $line nie zawiera nowych linii. Zawiera ona instancje , a następnien. Widać to wyraźnie z:

$ 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

Zastępstwo polega na zastąpieniu tych, w których jest miejsce, co wystarczy, aby zadziałały pętle:

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

Możesz również najpierw przekonwertować zmienną na tablicę, a następnie iterować nad tym.

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

Jest to użyteczne tylko wtedy, gdy nie chcesz zadzierać z IFS i mieć problemów z komendą read, jak to może się zdarzyć, jeśli wywołasz inny skrypt wewnątrz pętli, że skrypt ten może opróżnić bufor odczytu przed powrotem, jak to się stało ze mną.

Advertisement

Pytania pokrewne

11
6
9
19
8
Advertisement