Почему я получаю разные значения $x
из приведенных ниже фрагментов?
#!/bin/bash
x=1
echo fred > junk ; while read var ; do x=55 ; done < junk
echo x=$x
# x=55 .. I'd expect this result
x=1
cat junk | while read var ; do x=55 ; done
echo x=$x
# x=1 .. but why?
x=1
echo fred | while read var ; do x=55 ; done
echo x=$x
# x=1 .. but why?
Ответы:
Правильные объяснения уже были даны jsbillings и geekosaur , но позвольте мне немного остановиться на этом.
В большинстве оболочек, включая bash, каждая сторона конвейера работает в подоболочке, поэтому любое изменение внутреннего состояния оболочки (например, установка переменных) остается ограниченным этим сегментом конвейера. Единственная информация, которую вы можете получить из подоболочки - это то, что она выводит (для стандартного вывода и других файловых дескрипторов) и ее код выхода (который находится в диапазоне от 0 до 255). Например, следующий фрагмент печатает 0:
В ksh (варианты, полученные из кода AT & T, а не в вариантах pdksh / mksh) и zsh, последний элемент конвейера выполняется в родительской оболочке. (POSIX допускает оба поведения.) Таким образом, фрагмент выше печатает 2.
Полезной идиомой является включение в конвейер продолжения цикла while (или того, что у вас есть в правой части конвейера, но цикл while на самом деле здесь обычен):
источник
< <(locate -ber ^\.tag$)
, благодаря оригинальному немного неясному ответу и комментам geekosaur и glenn jackman .. Сначала я был в дилемме о принятии ответа, но нетто-результата было довольно ясно, особенно с последующим комментариемВы столкнулись с проблемой переменной области. Переменные, определенные в цикле while, который находится с правой стороны канала, имеют свой собственный локальный контекст области видимости, и изменения в переменной не будут видны за пределами цикла. Цикл while по сути является подоболочкой, которая получает копию среды оболочки, и любые изменения в среде теряются в конце оболочки. Смотрите этот вопрос StackOverflow .
ОБНОВЛЕНО : я не упомянул о важном факте, что цикл while со своим собственным подоболочком был обусловлен тем, что он является конечной точкой канала, я обновил это в ответе.
источник
while
цикл как хвостовую часть конвейера, который бросает его в подоболочку.blah|blah|while read ...
, вы можете иметьwhile read ...; done < <(blah|blah)
Как упоминалось в других ответах , части конвейера работают в подоболочках, поэтому сделанные в них модификации не видны основной оболочке.
Если мы рассмотрим только Bash, в дополнение к
cmd | { stuff; more stuff; }
структуре есть два других обходных пути :Перенаправить ввод от подстановки процесса :
Вывод команды in
<(...)
выглядит так, как если бы это был именованный канал.lastpipe
Вариант, который делает Bash работу как KSH, и запускает последнюю часть трубопровода в основном процессе оболочки. Хотя это работает, только если управление заданиями отключено, т.е. не в интерактивной оболочке:или
Подстановка процессов также поддерживается в ksh и zsh. Но поскольку в любом случае они запускают последнюю часть конвейера в основной оболочке, использовать ее в качестве обходного пути не обязательно.
источник
это может работать.
источник