Чому встановлення змінної перед командою legal у bash?


68

Я щойно стикався з кількома відповідями, такими як аналіз розмежованого текстового файлу ..., в якому використовується конструкція:

while IFS=, read xx yy zz;do
    echo $xx $yy $zz
done < input_file

де IFSзмінна встановлюється перед readкомандою.

Я читав посилання на баш, але не можу зрозуміти, чому це законно.

я намагався

$ x="once upon" y="a time" echo $x $y

з командного рядка bash, але нічого не відлунало. Чи може хтось вказати мені, де цей синтаксис визначений у посиланні, що дозволяє таким чином встановлювати змінну IFS? Це окремий випадок чи я можу зробити щось подібне w / інші змінні?


Відповіді:


69

Це під Граматикою Shell, Прості команди (наголос додано):

Проста команда - це послідовність необов'язкових призначень змінних, за якими слідують порожні слова та перенаправлення , і закінчується оператором управління. Перше слово вказує команду , яка буде виконана, і передається в якості аргументу нуль. Решта слів передаються як аргументи викликаній команді.

Таким чином, ви можете передавати будь-яку змінну, яка вам захочеться. Ваш echoприклад не працює, тому що змінні передаються команді, а не встановлені в оболонці. Оболонка розширюється $xі $y перед тим, як викликати команду. Це працює, наприклад:

$ x="once upon" y="a time" bash -c 'echo $x $y'
once upon a time

Дякую! Перш ніж запитати, я багато робив гугл і намагався розібратися, де сказано, що в довідці, без удачі. Я намагаюся покращитись написанням баш-скриптів, і ваша відповідь допомагає.
Майк Ліпперт

1
Хм, я думаю, мені потрібно знайти кращу довідку, моя (див. Посилання вище) не говорить про те, що в ній немає розділу «Граматика оболонки».
Майк Ліпперт

1
@MikeLippert Див. 3.7.4 у цій посиланні ("Навколишнє середовище для будь-якої простої команди чи функції може бути тимчасово доповнене, додавши її до призначення параметрів"). Я думаю, що посилання є на більш старій версії bash. Я щойно бігав man bashза своєю системою ...
derobert

29

Визначені змінні стають схожими на змінні середовища у роздвоєному процесі.

Якщо ти біжиш

A="b" echo $A

то bash спочатку розширюється $Aна, ""а потім працює

A="b" echo

Ось правильний спосіб:

x="once upon" y="a time" bash -c 'echo $x $y'

Зверніть увагу на одиничні цитати bash -c, інакше у вас є та сама проблема, як вище.

Таким чином, ваш приклад циклу є законним, тому що команда bash вбудована 'read' буде шукати IFS у змінних його середовища та знаходити ,. Тому

for i in `TEST=test bash -c 'echo $TEST'`
do
  echo "TEST is $TEST and I is $i"
done

буде надруковано TEST is and I is test

Нарешті, що стосується синтаксису, в циклі for for очікується рядок. Тому мені довелося використовувати зворотні посилання, щоб перетворити його в команду. Однак, поки циклі очікують синтаксис команд, наприклад IFS=, read xx yy zz.


1
Дякую, я дізнався трохи більше, ніж я просив з вашої відповіді. Я також проголосував би за вашу відповідь, але мені ще не дозволено, і я позначив прийняту відповідь на 1-й.
Майк Ліпперт

1
Спасибі, я на це я сподівався. І голосування є менш значущим, ніж почути вашу оцінку!
Майк Ферхерст

Щоб уточнити ваш коментар під першим рядком коду: Так, із невідомою Aзмінною bash розширюється $Aна порожню рядок, але для уникнення плутанини я б не використовував, ""оскільки код не еквівалентний A="b" echo "". Аргументу на це не буде echo.
пабук

8

man bash

СЕРЕДОВИЩЕ

[...] Навколишнє середовище для будь-якої простої команди чи функції може бути тимчасово доповнене, додавши її до призначення параметрів, як описано вище в PARAMETERS. Ці заяви про присвоєння впливають лише на середовище, яке бачить ця команда.

Змінні розширюються до того, як відбудеться призначення змінної. З очевидної причини, яка var=xпрацювала б і іншим шляхом, але var=$othervarне буде. Тобто ваш $xпотрібен до того, як він буде доступний. Але це не головна проблема. Основна проблема полягає в тому, що командний рядок може змінюватися тільки середовищем оболонки, але призначення не стає частиною середовища оболонки.

Ви змішуєте функції: Ви хочете замінити командний рядок, але помістіть змінне визначення у середовище команд. Заміна командного рядка повинна бути виконана оболонкою. Навколишнє середовище повинно бути явно використане викликаною командою. Буде і як це робиться, залежить від команди.

Перевага цього використання полягає в тому, що ви можете встановити середовище для підпроцесу, не впливаючи на середовище оболонки.

x="once upon" y="a time" bash -c 'echo $x $y'

працює так, як ви очікували, оскільки в цьому випадку обидві функції поєднуються: Заміна командного рядка здійснюється не оболонкою, що викликає, а оболонкою підпроцесу.


1
Це трохи тонкіше, ніж це, тому що приклад також працює, x="once upon" y="a time" eval 'echo $x $y'коли немає ніякого підпроцесу, оскільки evalє вбудованим. Я здогадуюсь, відповідна цитата на сторінці сторінки є The environment for any simple command or function may be augmented temporarily by prefixing it with parameter assignments. Розглядаючи приклад питання, він повинен бути таким, оскільки readвін також є вбудованим і працює з тимчасово зміненим станом IFS.
Девід Онгаро

4

Команда ви надасте відрізняється тим , що $xі $yрозширена до того в echoзапуску команди, тому їх значення в поточній оболонці використовується, а НЕ значення , які echoбудуть бачити в своєму середовищі , якби це було дивитися.


Як можна розширити що-небудь у командному рядку після запуску команди ...?
Hauke ​​Laging

Призначення перед командами для xта yдля середовища, яке echoпрацює, а не середовища, в якому аргументи повинні echoбути розширені. Бо IFS=, read xx yy zzвся readкоманда зчитується, нерозкладеною, командою. Потім , цей рядок розділена в відповідності зі значенням IFS, з відповідними частинами , привласнених xx, yyі zz.
чепнер

Я просто хотів зазначити, що формулювання "розширено до запуску команди" не має особливого сенсу, оскільки після запуску команди більше нічого не розширюється. Крім того: Ви не мали жодного погляду на мою відповідь чи все-таки ви вважаєте, що мені потрібно пояснення того, що відбувається ...?
Hauke ​​Laging

1
Я не стверджував, що вам потрібне пояснення, і що що-небудь можна розширити після запуску команди. Однак, правда, що bashспочатку аналізує даний командний рядок, визнає, що є два змінних призначення, які слід застосувати до середовища команди, що надходить, ідентифікує команду для запуску ( echo), розширює будь-які параметри, знайдені в аргументах, а потім виконує команду echoз розширеними аргументами.
чепнер

У випадку echoя не був впевнений, чи може вона "бачити" змінну, оскільки це вбудована команда, а отже, не працює в нижній частині, яка може мати власне оточення. Але я спробував це, з evalяким також є вбудований, і він справді знає про це. Наприклад, спробуйте, a=xyz eval 'echo $BASHPID $a; grep -z ^a /proc/$BASHPID/{,task/*}/environ'; echo $BASHPID $aщо показує, що aвстановлено лише в межах, evalхоча pid однаковий, а середовище не змінюється під час eval! (Для того, щоб отримати доступ, /procвам потрібно запустити це під Linux.) Здається, що тут bash робить додаткову магію.
Девід Онгаро

2

Я хочу розширити картину " чому це законно"

Відповідь: щоб ви могли зателефонувати або викликати програму, а для цього виклику використовувати лише змінну з певною змінною.

Як приклад: у вас є параметр для підключення до бази даних, який називається "db_connection", і зазвичай ви передаєте "test" як ім'я для підключення до вашої тестової бази даних. Насправді ви навіть можете встановити його за замовчуванням, яке вам не потрібно буде явно вводити. Однак іноді ви хочете працювати з базою даних ci. Таким чином, ви передаєте парам як "ci", а потім викликана програма використовує цей параметр бази даних як ім'я db, яке використовується для всіх дзвінків до бази даних. Для наступного запуску, якщо ви не повторите підхід і просто подзвоните програмі, змінна повернеться до свого попереднього значення за замовчуванням.


0

Ви також можете використовувати ;. Він буде оцінюватися раніше, оскільки це розділювач команд.

x="once upon" y="a time"; echo $x $y

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