Используйте встроенную функцию bash read без цикла while

Используйте встроенную функцию bash read без цикла while

Я привык к bashвстроенной readфункции в циклах while, например:

echo "0 1
      1 1
      1 2
      2 3" |\
while read A B; do
    echo $A + $B | bc;
done

Я работал над каким-то makeпроектом, и стало разумным разбить файлы и сохранить промежуточные результаты. В результате я часто разрываю отдельные строки на переменные. Хотя следующий пример работает довольно хорошо,

head -n1 somefile | while read A B C D E FOO; do [... use vars here ...]; done

это как-то глупо, потому что цикл while никогда не будет выполняться больше одного раза. Но без while,

head -n1 somefile | read A B C D E FOO; [... use vars here ...]

Переменные чтения всегда пусты, когда я их использую. Я никогда не замечал такого поведения read, потому что обычно я использую циклы while для обработки множества похожих строк. Как я могу использовать встроенную bashфункцию readбез цикла while? Или есть другой (или даже лучший) способ прочитать одну строку в несколько (!) переменных?

Заключение

Ответы учат нас, что это проблема масштаба. Заявление

 cmd0; cmd1; cmd2 | cmd3; cmd4

интерпретируется таким образом, что команды cmd0, cmd1и cmd4выполняются в одной и той же области, в то время как команды cmd2и cmd3каждой из них присваивается своя собственная подоболочка и, следовательно, разные области действия. Исходная оболочка является родительской для обеих подоболочек.

решение1

Это потому, что часть, где вы используете vars, является новым набором команд. Используйте это вместо этого:

head somefile | { read A B C D E FOO; echo $A $B $C $D $E $FOO; }

Обратите внимание, что в этом синтаксисе после должен быть пробел, { а ;перед }. -n1Это также не обязательно; readсчитывается только первая строка.

Для лучшего понимания вам может помочь следующее; это делает то же самое, что и выше:

read A B C D E FOO < <(head somefile); echo $A $B $C $D $E $FOO

Редактировать:

Часто говорят, что следующие два утверждения делают одно и то же:

head somefile | read A B C D E FOO
read A B C D E FOO < <(head somefile)

Ну, не совсем. Первый — это канал от headк bashвстроенному read. Stdout одного процесса к stdin другого процесса.

Второе выражение — перенаправление и замена процесса. Оно обрабатывается само по bashсебе. Оно создает FIFO (именованный канал, <(...)), headк которому подключен выход , и перенаправляет ( <) его в readпроцесс.

Пока что они кажутся эквивалентными. Но при работе с переменными это может иметь значение. В первом случае переменные не устанавливаются после выполнения. Во втором случае они доступны в текущей среде.

У каждой оболочки свое поведение в этой ситуации. Смотритеэта ссылкадля которых они есть. В bashвы можете обойти это поведение с помощью группировки команд {}, подстановки процессов ( < <()) или строк Here ( <<<).

решение2

Цитата из очень полезной статьиwiki.bash-hackers.org:

Это происходит потому, что команды конвейера выполняются в подоболочках, которые не могут модифицировать родительскую оболочку. В результате переменные родительской оболочки не модифицируются (см. статью:Bash и дерево процессов).

Поскольку ответ уже был дан несколько раз, альтернативный способ (с использованием не встроенных команд...) таков:

$ eval `echo 0 1 | awk '{print "A="$1";B="$2}'`;echo $B $A
$ 1 0

решение3

Как вы отметили, проблема была в том, что канал to readбыл запущен в подоболочке.

Один из ответов — использоватьхередок:

numbers="01 02"
read first second <<INPUT
$numbers
INPUT
echo $first
echo $second

Этот метод хорош тем, что он будет вести себя одинаково в любой POSIX-подобной оболочке.

Связанный контент