Bash: Iterando as linhas em uma variável

175

Como uma pessoa itera corretamente sobre linhas no bash em uma variável ou na saída de um comando? Simplesmente definir a variável IFS para uma nova linha funciona para a saída de um comando, mas não ao processar uma variável que contenha novas linhas.

Por exemplo

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

Isto dá a saída:

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

Como você pode ver, ecoar a variável ou iterar sobre o comando cat imprime cada uma das linhas uma por uma corretamente. No entanto, o primeiro loop for imprime todos os itens em uma única linha. Alguma idéia?

    
por Alex Spurling 16.05.2011 / 14:14

4 respostas

224

Com o bash, se você quiser incorporar novas linhas em uma string, coloque a string com $'' :

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

E se você já tiver uma string como essa em uma variável, poderá lê-la linha por linha com:

while read -r line; do
    echo "... $line ..."
done <<< "$list"
    
por 16.05.2011 / 15:47
51

Você pode usar while + read :

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

Entre. a opção -e para echo não é padrão. Use printf , se você quiser portabilidade.

    
por 16.05.2011 / 14:21
21
#!/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
    
por 17.03.2014 / 22:45
7

Aqui está uma maneira engraçada de fazer o seu loop:

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

Um pouco mais sensível / legível seria:

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

Mas isso é muito complexo, você só precisa de um espaço:

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

A variável $line não contém novas linhas. Ele contém instâncias de \ seguidas por %código%. Você pode ver isso claramente com:

$ 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

A substituição está substituindo aqueles com espaços, o que é suficiente para trabalhar em loops forçados:

$ 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
    
por 16.05.2011 / 14:45

Tags