Почему «A = 10 echo $ A» не печатает 10?

25

Эта команда:

A=10 echo $A

печатает пустую строку Почему нет 10? Почему не работает временная настройка среды?

Я хочу знать причину и объяснение, а не решение.

я использовал

LANG=C gcc ...

чтобы заставить gcc использовать системный язык (английский) вместо системного (китайский). Поэтому я предполагаю, что VAR=valueпрефикс настроит временную среду для следующей за ним команды. Но похоже у меня какое-то недопонимание.

Земля Двигатель
источник

Ответы:

22

Это вопрос порядка, в котором выполняются различные этапы оценки команды.

A=10 echo $Aсначала разбирает команду на простую команду, состоящую из трех слов A=10, echoи $A. Затем каждое слово подвергается подстановке переменных, то есть трансформации расширений переменных, таких как$A в их значения (я опускаю шаги, которые ничего не делают видимыми).

Если Aимеет значение на fooначальном этапе, результат шагов расширения является простой командой , которая до сих пор имеет три слова: A=10, echoи foo. (В этот момент оболочка также запоминает, какие символы изначально были в кавычках, в данном случае - ни одного.) Следующим шагом является выполнение команды. Поскольку A=10начинается с правильного имени переменной, за которым следует знак равенства, оно рассматривается как присваивание; переменная Aустанавливается 10как в оболочке, так и в среде во время выполнения команды. (Обычно вам нужно написатьexport A чтобы иметь Aв среде, а не просто как переменную оболочки; это исключение.) Следующее слово не является присваиванием, поэтому оно рассматривается как имя команды (это встроенная команда). echoКоманда не зависит от какой-либо переменной, поэтому A=10 echo $Aимеет тот же эффект, что и echo $A.

Если вы хотите установить переменную только на время выполнения команды, но с учетом назначения при выполнении команды, вы можете использовать подоболочку. Подоболочка, обозначенная круглыми скобками, выполняет все изменения состояния (назначения переменных, текущий каталог, определения функций и т. Д.) Локально для подоболочки.

(A=10; echo $A)

Сделайте это, export A=10если хотите экспортировать переменную в среду, чтобы ее видели внешние программы.

Жиль "ТАК - перестань быть злым"
источник
Спасибо, могу я сказать A=10 (echo $A)и получить 10?
Земля Двигатель
2
@EarthEngine Нет, это будет синтаксическая ошибка. Назначение должно быть в начале простой команды (т. Е. Просто имя команды и некоторые параметры, а также некоторые начальные назначения и некоторые перенаправления). A=10; (echo $A)выводит, 10но также устанавливает Aдля оставшейся части сценария.
Жиль "ТАК - перестать быть злым"
2
@EarthEngine Но вы можете сказать A=10 eval 'echo $A'. Одиночные кавычки перестают $Aинтерпретироваться до тех пор, пока не будет вычислена вся строка ... К этому времени A = 10. Я считаю этот ответ более правильным, чем принятый.
Оли
Я думаю, что это правильное объяснение. Причиной такого поведения является лишь порядок того, когда происходит расширение $Aи присвоение A. Например, не A=5; A=6 let 'a=A'; echo $aвозвращается, и я не думаю, что запускает подоболочку, так как это встроенная команда. 65let
Дэвид Онгаро
@EarthEngine: Когда это говорит о том , что правильное объяснение является порядок оценки, он может ввести в заблуждение: A=10 echo $Aбудет не установлен A=10для любых команд , после этого, даже если они находятся в разных строках (когда ясно назначение было уже оценено). Речь идет не о порядке, а о масштабах
MestreLion
37

При использовании того, LANG=C gcc ... что происходит , является то, что оболочка устанавливает LANG для gccокружающей среды «S только и не для текущей самой среды ( смотрите примечание ). Таким образом, после gccзавершения LANGвозвращается к своему предыдущему значению (или не установлено).

Кроме того, при использовании A=10 echo $Aименно оболочка заменяет $ A, а не echo, и эта подстановка (называемая «расширением») происходит до того, как вычисляется оператор (включая присвоение), поэтому для работы с ожидаемым Aзначением должно быть уже установлено в нынешних условиях до этого заявления.

Вот почему A=10 echo $Aне работает должным образом: A=10будет установлено для echo, но echo внутренне игнорирует значение переменной окружения A. И $Aзаменяется значением, установленным в текущей оболочке (которого нет), а затем передается в качестве аргумента echo.

Так что ваше предположение верно: VAR=value command делает работу, но это имеет значение только если commandвнутренне использует УАК. Если нет, то вы можете передать в valueкачестве аргумента к command, но аргументы заменяются текущей оболочкой, поэтому они должны быть установлены перед использованием:VAR=value; command "$VAR"

Если вы знаете, как создать исполняемый скрипт, вы можете попробовать это как тест:

#!/bin/sh
echo "1st argument is $1"
echo "A is $A"

Сохраните как testscriptи попробуйте:

$ A=5; A=10 testscript "$A"; echo "$A"
1st argument is 5
A is 10
5

И последнее, но не менее важное: стоит знать разницу между переменными оболочки и среды и аргументами программы .

Вот несколько хороших ссылок:

,

(*) Примечание: технически оболочка также устанавливается в текущей среде, и вот почему: некоторые команды, такие как echo, readи testявляются встроенными в оболочку , и поэтому они не порождают дочерний процесс. Они бегут в текущей среде. Но оболочка заботится о назначении только до тех пор, пока команда не будет запущена, поэтому для всех практических целей эффект тот же: назначение видится только этой единственной командой.

MestreLion
источник
2
Это объяснение на самом деле неверно, хотя оно приводит к правильному выводу во всех, кроме нескольких ключевых случаях. Реальное объяснение - это порядок раскрытия: $Aоценивается до того, как присвоение произойдет. Я думаю, что ваше объяснение терпит неудачу только в случае обычных встроенных утилит, поведение которых зависит от значения переменной: встроенное действительно видит назначенное значение. Типичным примером является IFS=: read one two three restчтение полей, разделенных двоеточиями: readвстроенная команда видит значение IFS.
Жиль "ТАК - перестань быть злым"
«Не для текущей оболочки» неверно: переменная установлена ​​в текущей оболочке, но действует только для текущей простой команды. echoувидел бы значение 10для A, если бы это заботилось.
Жиль "ТАК - перестань быть злым"
@ Жиль: большое спасибо за разъяснения! Я не знал об этой тонкости. Так что, если я правильно понял, bash должен установить текущее окружение, иначе встроенные функции (которые не порождают новое pid) не увидят назначение, как другие команды "regurlar". Но он сбрасывается после выполнения команды, чтобы ограничить область назначения. Это правильно, я исправлю свой ответ соответственно. PS: в сторону технических деталей, я все еще думаю, что ответ должен подчеркивать аспект объема, а не порядок оценки, в противном случае можно подумать, что A=10 test; echo $Aбудет напечатано 10
MestreLion
3

Один, возможно, чистый способ сделать то, что вы, очевидно, желаете, это выполнить команду:

A=10 eval 'echo $A'

Что фактически отменит замену значения 10 вместо $ A на более поздний контекст (т. Е. «Внутри» eval, который уже знает о назначении). Обратите внимание, что одинарные кавычки необходимы. Такая конструкция четко передает назначение вашей желаемой команде (в данном случае эхо) без риска загрязнения вашей текущей среды.

nhokka
источник