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

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

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

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

 head -n1 somefile | while read ABCDE FOO; do [... use vars here ...]; done 

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

 head -n1 somefile | read ABCDE FOO; [... use vars here ...] 

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

Вывод

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

  cmd0; cmd1; cmd2 | cmd3; cmd4 

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

3 Solutions collect form web for “Использовать встроенный встроенный bash без цикла while”

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

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

Обратите внимание, что в этом синтаксисе должно быть пробел после { и a ; (точка с запятой) перед } . Также -n1 не требуется; Только чтение читает первую строку.

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

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

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

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

 head somefile | read ABCDE FOO read ABCDE FOO < <(head somefile) 

Ну, не совсем. Первый – это труба от head до встроенного read bash . Один вывод stdout одного процесса в stdin другого процесса.

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

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

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

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

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

Поскольку ответ был предоставлен несколько раз, альтернативный способ (с использованием не встроенных команд …) заключается в следующем:

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

Как вы заметили, проблема заключалась в том, что труба для read запускается в подоболочке.

Один из ответов – использовать heredoc :

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

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

  • Как я могу заставить переменные среды «экспортироваться» в скрипт оболочки?
  • Ограничьте каталоги, в которых будет действовать скрипт
  • Ошибка в команде «date -d»
  • Фильтрация входов в текстовый файл
  • Удалять строки из одного файла, если они содержат регулярное выражение содержимого в другом файле
  • обработка текста: извлечение частей файла и запись их в одну строку
  • Создайте новую ссылку для доступа ко всем файлам и папкам
  • удалите строку, если та же строка существует в предыдущей строке
  • Как переименовать определенные каталоги, если у родителя есть определенное имя?
  • Как учесть env vars shell-session, контексты с chroot
  • Bash присоединяется к файлу, удаляется во время выполнения. Можно ли это исправить?
  • Определить набор значений для переменной в сценарии оболочки
  • Linux и Unix - лучшая ОС в мире.