Leitura Bash: Lendo lista separada por vírgula, o último elemento está ausente

8

A saída do comando abaixo é estranha para mim. Por que não me devolve o elemento 5?

$ echo '0,1,2,3,4,5' | while read -d, i; do echo $i; done
0
1
2
3
4

Eu esperaria que '5' fosse retornado também. Em execução GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu). A adição de uma vírgula funciona, mas meus dados de entrada não têm uma vírgula. Estou esquecendo de algo?

Karlo
fonte

Respostas:

12

With read, -dé usado para terminar as linhas de entrada (ou seja, para não separar as linhas de entrada). Sua última "linha" não contém terminador, então readretorna false no EOF e o loop sai (mesmo que o valor final tenha sido lido).

echo '0,1,2,3,4,5' | { while read -d, i; do echo "$i"; done; echo "last value=$i"; }

(Mesmo com -d, readtambém usa $IFS, espaço em branco absorvente, incluindo \no resultado final que apareceria usando outros métodos, como readarray)

O FAQ do Bash discute isso e como lidar com vários casos semelhantes:

mr.spuratic
fonte
8
Eu acho que alguém poderia fazer read -d, i || [[ -n $i ]]a la O que while read -r line || [[ -n $line ]]significa?
Steeldriver 17/05/19
8

Como outras respostas afirmam, -dé um caractere de fim de linha, não um separador de campos. Você pode fazer

IFS=, read -a fields <<< "1,2,3,4,5"
for i in "${fields[@]}"; do echo "$i"; done
Graham Breed
fonte
5

Do homem:

-d delim

O primeiro caractere de delim é usado para finalizar a linha de entrada, em vez de nova linha.

Seu elemento 5 não possui um delimitador (vírgula), portanto não será lido.

Siva
fonte
Portanto, a melhor solução é colocar outra vírgula após a entrada?
Karlo
3
A melhor solução pode ser processar dados com algo diferente de um shell . Como os respondentes aqui abordaram a pergunta atual, você pode considerar uma pergunta separada que demonstra seu objetivo maior.
Jeff Schaller
3

O que você está vendo é o mesmo comportamento (e pelo mesmo motivo) de Por que esse loop 'while' não reconhece a última linha?

Como nesse caso, você pode modificar o comportamento adicionando um teste extra à condição de finalização do loop, da seguinte maneira

while read -d, i || [[ -n $i ]]; do ...

Ex.

$ echo '0,1,2,3,4,5' | while read -d, i || [[ -n $i ]]; do echo $i; done
0
1
2
3
4
5
chave de aço
fonte