Чому "A = 10 ехо $ 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в оточенні, а не просто як змінну оболонки; це виняток.) ​​Наступне слово не є призначенням, тому воно трактується як ім'я команди (це вбудована команда). Theechoкоманда не залежить від будь-якої змінної, тому 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, а не відлуння, і ця заміна (називається "розширення") відбувається до того, як оператор буде оцінено (включаючи призначення), тому для роботи, як очікувалося A, значення повинно бути вже встановлено в поточному середовищі до цього твердження.

Ось чому A=10 echo $Aце не працює, як очікувалося: A=10буде встановлено для echo, але echo внутрішньо ігнорує значення змінної середовища A. І $Aзамінюється значенням, встановленим у поточній оболонці (яке немає), а потім передається як аргумент на лунку.

Таким чином, ваше припущення правильне: VAR=value command чи працює, але це актуально лише у випадку, коли commandвнутрішньо використовується VAR. Якщо немає, то ви можете передати в 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є оболонки вбудованих функцій , і як такі вони не породжують дочірній процес. Вони працюють у поточному середовищі. Але оболонка піклується про те, щоб призначення не тривало до тих пір, поки команда не запуститься, тому для всіх практичних цілей ефект однаковий: призначення бачиться лише за допомогою тієї самої команди.


2
Це пояснення насправді невірно, хоча воно призводить до правильного висновку у всіх, крім кількох кутових випадках. Справжнє пояснення - порядок розширення: $Aоцінюється до того, як відбудеться завдання. Я думаю, що ваше пояснення провалюється лише у випадку звичайних вбудованих утиліт, поведінка яких залежить від значення змінної: вбудований бачить призначене значення. Поширений приклад IFS=: read one two three rest, який читає розділені двокрапкою поля: readвбудований бачить значення IFS.
Жиль "ТАК - перестань бути злим"

"Не для самої поточної оболонки" помиляється: змінна встановлюється в поточній оболонці, але вона триває лише для поточної простої команди. echoпобачив би значення 10для цього A, якби він піклувався.
Жил "ТАК - перестань бути злим"

@Gilles: дякую за роз’яснення! Я не усвідомлював цієї тонкощі. Отже, якщо я правильно зрозумів, Баш має в набір для поточної середовища в іншому випадку (вбудовані функції, які не породжують нові pid) не бачитиме призначення як інші «regurlar» команда буде. Але він скасовується після виконання команди для обмеження області призначення. Це правильно, я відповіді виправлю відповідно. PS: технічні характеристики убік, я все ще думаю, що у відповіді слід наголосити на аспекті застосування, а не на порядку оцінювання, інакше можна подумати A=10 test; echo $A, що надрукується 10
MestreLion

3

Один, можливо, чистий спосіб зробити те, що ви, очевидно, бажаєте, - це зробити команду:

A=10 eval 'echo $A'

Що насправді відкладе підстановку значення 10 на місце $ A на більш пізній контекст (тобто "всередині" eval, який вже знає про призначення). Зауважте, що єдині цитати є важливими. Така конструкція чітко передає завдання бажаній команді (відлуння в цьому випадку), не ризикуючи забруднити ваше поточне середовище.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.