Почему "echo foo | read a; echo $ a" не работает должным образом?

Я мог воспроизвести проблему с различными оболочками под FreeBSD, GNU / Linux и Solaris. Это заставило меня чесать голову больше часа, поэтому я решил опубликовать вопрос здесь.

Если 'foo' - константа, тогда лучше всего работает a = foo. Итак, это не ваша настоящая проблема; это чрезмерно упрощенная версия вашей реальной проблемы.

Jonathan Leffler 17.12.2008 18:36

это работает так, как ожидалось - вы просто ожидаете не того;)

user3850 25.12.2008 16:37
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
8
2
8 434
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Ответ принят как подходящий

Из-за обвязки read выполнен в собственной подоболочке.

echo foo | while read a; do echo $a; done

будет делать то, что вы от него ожидаете.

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

Paul Tomblin 17.12.2008 17:42

Я все еще не могу его использовать, потому что $ a теряет свое значение за пределами блока while! эхо фу | пока читал; сделать echo $ a; Выполнено ; эхо $ а

Diomidis Spinellis 17.12.2008 17:54

@Diomidis, так что поместите все, что вам нужно сделать с $ a в цикл.

Paul Tomblin 17.12.2008 17:59

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

Diomidis Spinellis 17.12.2008 18:02

Вы должны были рассказать нам больше о своей проблеме. Ваш первоначальный вопрос был просто «почему это не работает?» (И был дан ответ), но мы, вероятно, могли бы помочь вам намного лучше, если бы знали больше о вашей проблеме.

Bombe 17.12.2008 18:21

Ты прав. Трудно найти баланс между слишком общим и слишком конкретным.

Diomidis Spinellis 17.12.2008 23:48

эхо фу | { прочитать; echo $ a; } делает то же самое без while.

user3850 25.12.2008 16:39

Все они делают 'foo' присвоенным переменной в подоболочке, но запрашивающий хочет, чтобы переменная в родительской оболочке была назначена

xiaodong 27.02.2016 06:59

альтернатива:

echo foo | (read a ; echo $a)

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

Если вам нужен $ a вне подоболочки, вам нужно отменить команды:

read a < <(echo foo); echo $a

таким образом чтение выполняется в текущем процессе

Однако это не позволяет мне использовать значение вне подоболочки.

Diomidis Spinellis 17.12.2008 17:41

Хорошо, но обратите внимание, что <() не является синтаксисом POSIX.

daf 31.12.2009 03:22
read a <<<"foo"; echo $a также работает и не создает подоболочку и именованный канал, например <()
Asfand Qazi 11.03.2020 15:59

read ожидает ввода с новой строки

while read a; do echo $a; done
foo
bar
^D

более или менее то, что вы хотели

По словам Кернигана и Пайка Среда программирования Unix (стр. 159), «ни одна из встроенных команд оболочки (в отличие от примитивов потока управления, например for) не может быть перенаправлена ​​с помощью> и <», и «это можно описать как ошибку. в оболочке ». Кажется, это объясняет а) почему такой код

ls *.c |
while read file
do
   echo $a
done

неизменно работает без проблем, и б) несогласованность в том, что перенаправление из файла работает.

нет, это не объясняет. ваша проблема - это просто проблема определения объема.

user3850 25.12.2008 16:41

Не могли бы вы объяснить масштабы, связанные с ответом?

Diomidis Spinellis 26.12.2008 15:33

в bash канал вызывает выполнение следующей команды в подоболочке. переменная среды, которую вы установили с помощью read, уничтожается, как только эта подоболочка завершается (в точке; между чтением и эхом), и, следовательно, недоступна для следующей команды.

user3850 30.12.2008 21:47

Это хорошее объяснение, если изменить его на «канал вызывает выполнение следующей команды встроенный в подоболочке». (Внешние команды, которые являются частью конвейера, определенно не запускаются в подоболочке.)

Diomidis Spinellis 02.01.2009 15:30

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

user3850 03.01.2009 03:08

Кстати, на странице руководства сказано: «Каждая команда в конвейере выполняется как отдельный процесс (то есть в подоболочке)». Ничего о разнице между встроенным и не встроенным нет.

user3850 03.01.2009 03:10

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

Diomidis Spinellis 04.01.2009 22:38

Я не думаю, что это уже было сказано:

a=`echo foo`
echo $a

Конечно, если вы хотите прочитать несколько значений, вам нужно сделать так, как предлагает Бомб.

Arthur Reutenauer 17.12.2008 18:08

Я придумал решение, которое не скрывает значения переменных в подоболочке, а также может работать с несколькими значениями.

set `echo foo bar`
A=$1
B=$2

Если вы собираетесь использовать «set», всегда используйте «set -», чтобы гарантировать, что «-foo» вас не облажает.

Jonathan Leffler 17.12.2008 18:35

Просто к вашему сведению; в 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, если вы установите параметр lastpipeshopt -s lastpipe), он аналогичным образом выполнит последнюю команду канала в текущей оболочке, а не в подоболочке.

Chris Dodd 13.07.2014 08:51

| является оператором межпроцессное взаимодействие. Таким образом, неявно существует подпроцесс, который должен быть создан для синтаксического анализа и оценки выражения с одной или другой стороны. 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.

jlliagre 27.03.2012 09:28

Другие вопросы по теме