2015-10-22 7 views
3

У меня есть сценарий, из которого я возвращаю несколько значений из каждой строки в новой строке. Чтобы зафиксировать эти значения в качестве переменных bash, я использую встроенный read (как рекомендовано here).Функция чтения Bash возвращает код ошибки при использовании нового разделителя строк

Проблема заключается в том, что когда я использую новый символ линии в качестве разделителя для read, я, кажется, всегда получаю ненулевой код выхода. Это приводит к хаосу с остальными моими сценариями, которые проверяют результат операции.

Вот сокращенная версия того, что я делаю:

$ read -d '\n' a b c < <(echo -e "1\n2\n3"); echo $?; echo $a $b $c 
1 
1 2 3 

Обратите внимание на статус выхода 1.

Я не хочу, чтобы переписать мой сценарий (echo команда выше) использовать другой разделитель (поскольку имеет смысл использовать новые строки в других местах кода).

Как я могу получить read, чтобы играть хорошо и вернуть нулевой статус выхода, когда он успешно считывает 3 значения?

Update

Хм, кажется, что я могу быть с помощью "разделитель" ошибочно. От человека странице:

-d *delim* 

    The first character of delim is used to terminate the input line, 
    rather than newline. 

Таким образом, один способ, которым я мог бы достичь желаемого результата, чтобы сделать это:

read -d '#' a b c < <(echo -e "1\n2\n3\n## END ##"); echo $?; echo $a $b $c 

Может быть, есть более хороший путь, хотя?

+2

Вы должны написать '-d $ '\ n''. В противном случае разделителем является обратная косая черта (которая, вероятно, никогда не читается, так как вы не используете параметр '-r' для' read'). –

ответ

3

-d это неправильная вещь для использования здесь. То, что вы действительно хотите это три отдельные звонки следующим образом:

{ read a; read b; read c; } < <(echo $'1\n2\n3\n') 

Убедитесь, что входные концы с новой строки, так что конечный read имеет статус выхода 0.

Если вы не знаете, как многие строки находятся на входе раньше времени, вам нужно прочитать значения в массиве. В bash 4, который принимает только один вызов readarray:

readarray -t arr < <(echo $'1\n2\n3\n') 

До bash 4, вам нужно использовать цикл:

while read value; do 
    arr+=("$value") 
done < <(echo $'1\n2\n3\n') 

read всегда читает одну строку ввода ; опция -d изменяет read идея о том, что завершает линию. Пример:

$ while read -d'#' value; do 
> echo "$value" 
> done << EOF 
> a#b#c# 
> EOF 
a 
b 
c 
+0

Это замечательно. Я не думал использовать несколько 'read's. Благодаря! –

2

«Проблема» в том, что read возвращает ненулевой, когда она достигает EOF которая происходит, когда разделитель не в конце ввода.

Таким образом, добавление новой строки в конец вашего ввода сделает ее работу так, как вы ожидаете (и исправьте аргумент -d, как указано в комментарии пользователя gniourf_gniourf).

Что происходит в вашем примере, так это то, что read сканирует \ и ударяет EOF перед тем, как найти его. Затем входная линия делится на \n (из-за IFS) и присваивается $a, $b и $c. Затем read возвращает ненулевое значение.

Использование -d для этого является штраф, но \n является разделителем по умолчанию, так что вы ничего не меняется, если вы сделаете это, и если вы были получили разделителем правильный (-d $'\n'), в первую очередь вы бы видели ваш пример не работает вообще (хотя он бы вернул 0 из прочитанного). (См http://ideone.com/MWvgu7)

Обычной идиома при использовании read (в основном с нестандартными значениями -d является для проверки read «s возвращаемого значения и переменных присваиваются ли имеют значение. read -d '' line || [ "$line" ] к примеру. Который работает даже если сбой чтения на последней «строке» ввода из-за недостающего терминатора в конце.

Таким образом, чтобы ваш пример работал, вы хотите либо использовать несколько вызовов read так, как указано в chepner, либо (если вы действительно хотите один звонок), то вы хотите (см. http://ideone.com/xTL8Yn):

IFS=$'\n' read -d '' a b c < <(printf '1 1\n2 2\n3 3') 
echo $? 
printf '[%s]\n' "$a" "$b" "$c" 

И добавление \0 в конец входного потока (например, printf '1 1\n2 2\n3 3\0') или положить || [ "$a" ] в конец, избежать возврата отказа от вызова read.

Установка для IFS для чтения заключается в том, чтобы предотвратить оболочку от разбиения слов на пространствах и неправильное разбиение ввода. -d '' - read по телефону \0.