




Из-за обвязки read выполнен в собственной подоболочке.
echo foo | while read a; do echo $a; done
будет делать то, что вы от него ожидаете.
Вы знаете, у меня была именно такая проблема, я пришел к тому точному ответу (цикл while, который выполнялся только один раз) и никогда не понимал, почему это сработало. Спасибо.
Я все еще не могу его использовать, потому что $ a теряет свое значение за пределами блока while! эхо фу | пока читал; сделать echo $ a; Выполнено ; эхо $ а
@Diomidis, так что поместите все, что вам нужно сделать с $ a в цикл.
Да, я знаю, что могу это сделать, но это искажает код и не масштабируется. Я опубликовал еще один ответ о подходе, который я в конечном итоге использовал.
Вы должны были рассказать нам больше о своей проблеме. Ваш первоначальный вопрос был просто «почему это не работает?» (И был дан ответ), но мы, вероятно, могли бы помочь вам намного лучше, если бы знали больше о вашей проблеме.
Ты прав. Трудно найти баланс между слишком общим и слишком конкретным.
эхо фу | { прочитать; echo $ a; } делает то же самое без while.
Все они делают 'foo' присвоенным переменной в подоболочке, но запрашивающий хочет, чтобы переменная в родительской оболочке была назначена
альтернатива:
echo foo | (read a ; echo $a)
Редактировать:
Если вам нужен $ a вне подоболочки, вам нужно отменить команды:
read a < <(echo foo); echo $a
таким образом чтение выполняется в текущем процессе
Однако это не позволяет мне использовать значение вне подоболочки.
Хорошо, но обратите внимание, что <() не является синтаксисом POSIX.
read a <<<"foo"; echo $a также работает и не создает подоболочку и именованный канал, например <()
read ожидает ввода с новой строки
while read a; do echo $a; done foo bar ^D
более или менее то, что вы хотели
По словам Кернигана и Пайка Среда программирования Unix (стр. 159), «ни одна из встроенных команд оболочки (в отличие от примитивов потока управления, например for) не может быть перенаправлена с помощью> и <», и «это можно описать как ошибку. в оболочке ». Кажется, это объясняет а) почему такой код
ls *.c |
while read file
do
echo $a
done
неизменно работает без проблем, и б) несогласованность в том, что перенаправление из файла работает.
нет, это не объясняет. ваша проблема - это просто проблема определения объема.
Не могли бы вы объяснить масштабы, связанные с ответом?
в bash канал вызывает выполнение следующей команды в подоболочке. переменная среды, которую вы установили с помощью read, уничтожается, как только эта подоболочка завершается (в точке; между чтением и эхом), и, следовательно, недоступна для следующей команды.
Это хорошее объяснение, если изменить его на «канал вызывает выполнение следующей команды встроенный в подоболочке». (Внешние команды, которые являются частью конвейера, определенно не запускаются в подоболочке.)
спорный вопрос. покажите мне не встроенную команду, которая может изменить среду для любых следующих команд. читать имеет для встраивания
Кстати, на странице руководства сказано: «Каждая команда в конвейере выполняется как отдельный процесс (то есть в подоболочке)». Ничего о разнице между встроенным и не встроенным нет.
Вы правы насчет невстроенных команд, изменяющих среду, а также правы относительно страницы руководства; Я не знал об этом, я думал, что команды в конвейере порождаются непосредственно из родительской оболочки.
Я не думаю, что это уже было сказано:
a=`echo foo`
echo $a
Конечно, если вы хотите прочитать несколько значений, вам нужно сделать так, как предлагает Бомб.
Я придумал решение, которое не скрывает значения переменных в подоболочке, а также может работать с несколькими значениями.
set `echo foo bar`
A=$1
B=$2
Если вы собираетесь использовать «set», всегда используйте «set -», чтобы гарантировать, что «-foo» вас не облажает.
Просто к вашему сведению; в ksh все работает как положено; См. Также http://kornshell.com/doc/faq.html, Раздел III (вопросы программирования оболочки), Q13:
Q13. What is $bar after, echo foo | read bar?
A13. The is foo. ksh runs the last component of a pipeline
in the current process. Some shells run it as a subshell
as if you had invoked it as echo foo | (read bar).
В bash, если вы установите параметр lastpipe (с shopt -s lastpipe), он аналогичным образом выполнит последнюю команду канала в текущей оболочке, а не в подоболочке.
| является оператором межпроцессное взаимодействие. Таким образом, неявно существует подпроцесс, который должен быть создан для синтаксического анализа и оценки выражения с одной или другой стороны. Bash и более старые оболочки Bourne создают подпроцесс справа от оператора (чтение из канала), что означает, что любые установленные там переменные находятся только в области видимости до тех пор, пока этот процесс не завершится (что в этом примере кода находится в точке с запятой.
zsh и более новые версии оболочки Korn (по крайней мере, начиная с 93-го, но, возможно, даже до кш '88) будут создавать подпроцесс на другой стороне конвейера (записывая в него). Таким образом, они будут работать так, как задумал автор этого вопроса. («Как и ожидалось», конечно, очень субъективно. Понимание природы канала должно привести к тому, что можно ожидать, что это будет вести себя в соответствии с особенностями реализации).
Я не знаю, есть ли какое-либо конкретное положение в Posix, Spec 1170, SuS или любом другом опубликованном стандарте, требующем той или иной семантики. Однако на практике ясно, что нельзя зависеть от поведения; хотя вы можете легко проверить это в своих сценариях.
POSIX не указывает, какие стороны канала должны обрабатываться основной оболочкой. Таким образом, это неопределенное поведение. из pubs.opengroup.org/onlinepubs/009695399/utilities/…Additionally, each command of a multi-command pipeline is in a subshell environment; as an extension, however, any or all commands in a pipeline may be executed in the current environment. All other commands shall be executed in the current shell environment.
Если 'foo' - константа, тогда лучше всего работает a = foo. Итак, это не ваша настоящая проблема; это чрезмерно упрощенная версия вашей реальной проблемы.