Bash Read: чтение списка через запятую, последний элемент пропущен

8

Вывод команды ниже странный для меня. Почему он не возвращает мне элемент 5?

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

Я ожидаю, что «5» также будет возвращено. Запуск GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu). Добавление запятой работает, но у моих входных данных нет запятой. Я что-то пропустил?

Karlo
источник

Ответы:

12

С read, -dиспользуется для завершения строк ввода (т.е. не для разделения строк ввода). Ваша последняя «строка» не содержит терминатора, поэтому readвозвращает EOF значение false, и цикл завершается (даже если окончательное значение было прочитано).

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

(Даже с -d, readтакже использует $IFS, поглощая пробелы в том числе роспускного \nна конечное значение , которое будет отображаться с использованием других методов , таких как readarray)

Bash FAQ обсуждает это и как обрабатывать различные подобные случаи:

mr.spuratic
источник
8
Я думаю, что можно сделать read -d, i || [[ -n $i ]]а-ля. Что это while read -r line || [[ -n $line ]]значит?
SteelDriver
8

Как утверждают другие ответы, -dэто символ конца строки, а не разделитель полей. Ты можешь сделать

IFS=, read -a fields <<< "1,2,3,4,5"
for i in "${fields[@]}"; do echo "$i"; done
Грэм Порода
источник
5

От мужчины:

-делим

Первый символ delim используется для завершения строки ввода, а не перевода строки.

Ваш элемент 5 не имеет разделителя (запятой), поэтому он не будет прочитан.

Сива
источник
Так что лучше всего исправить еще одну запятую после ввода?
Карло
3
Наилучшее решение может быть данным процесс с чем - то другим , чем оболочкой . Поскольку ответчики обратились к текущему вопросу, вы можете рассмотреть отдельный вопрос, который демонстрирует вашу большую цель.
Джефф Шаллер
3

То, что вы видите, такое же поведение (и по той же причине), что и почему этот цикл while не распознает последнюю строку?

Как и в этом случае, вы можете изменить поведение, добавив дополнительный тест к условию завершения цикла:

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
steeldriver
источник