Bash Read: lecture de la liste séparée par des virgules, le dernier élément est manquant

8

La sortie de la commande ci-dessous est bizarre pour moi. Pourquoi ne me rend-il pas l'élément 5?

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

Je m'attendrais à ce que «5» soit également retourné. Courir GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu). L'ajout d'une virgule fonctionne, mais mes données d'entrée n'ont pas de virgule. Suis-je en train de manquer quelque chose?

Karlo
la source

Réponses:

12

Avec read, -dest utilisé pour terminer les lignes d'entrée (c'est-à-dire pour ne pas séparer les lignes d'entrée). Votre dernière "ligne" ne contient pas de terminateur, donc readretourne false sur EOF et la boucle se termine (même si la valeur finale a été lue).

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

(Même avec -d, readutilise également $IFS, absorber les espaces blancs, y compris le suivi \nde la valeur finale qui apparaîtrait en utilisant d'autres méthodes telles que readarray)

La FAQ Bash en discute et explique comment gérer divers cas similaires:

Mr Spuratic
la source
8
J'imagine que l'on pourrait faire read -d, i || [[ -n $i ]]un la Qu'est-ce que cela while read -r line || [[ -n $line ]]signifie?
steeldriver
8

Comme d'autres réponses l'indiquent, -dest un caractère de fin de ligne, pas un séparateur de champ. Tu peux faire

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

De l'homme:

-d délim

Le premier caractère de délimite est utilisé pour terminer la ligne d'entrée, plutôt que la nouvelle ligne.

Votre élément 5 n'a pas de délimiteur (virgule), il ne sera donc pas lu.

Siva
la source
Donc, la meilleure solution est de mettre une autre virgule après l'entrée?
Karlo
3
La meilleure solution pourrait être de traiter les données avec autre chose qu'un shell . Étant donné que les répondeurs ici ont répondu à la question actuelle, vous pouvez envisager une question distincte qui démontre votre objectif plus large.
Jeff Schaller
3

Ce que vous voyez est le même comportement (et pour la même raison) que Pourquoi cette boucle "while" ne reconnaît-elle pas la dernière ligne?

Comme dans ce cas, vous pouvez modifier le comportement en ajoutant un test supplémentaire à la condition de fin de boucle, comme suit

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
tournevis
la source