Встановлення змінної середовища перед командою в Bash не працює для другої команди в трубі


351

У даній оболонці, як правило, я встановлюю змінну чи змінні, а потім виконую команду. Нещодавно я дізнався про концепцію попередження визначення змінної для команди:

FOO=bar somecommand someargs

Це працює ... вид. Він не працює, коли ви змінюєте змінну LC_ * (що, здається, впливає на команду, але не на її аргументи, наприклад, діапазони знаків '[az]') або коли таким чином передає висновок іншій команді:

FOO=bar somecommand someargs | somecommand2  # somecommand2 is unaware of FOO

Я також можу додати somecommand2 також з "FOO = bar", що працює, але додає небажане дублювання, і це не допомагає аргументам, які інтерпретуються залежно від змінної (наприклад, '[az]').

Отже, який хороший спосіб зробити це в одному рядку?

Я щось думаю на замовлення:

FOO=bar (somecommand someargs | somecommand2)  # Doesn't actually work

Я отримав багато хороших відповідей! Мета полягає в тому, щоб зберегти цю однолінійку, бажано без використання "експорту". Метод, що використовує виклик до Bash, був найкращим в цілому, хоча дужка версії з "експортом" в ньому була трохи більш компактною. Також цікавий метод використання перенаправлення, а не труби.


1
(T=$(date) echo $T)буде працювати
vp_arth

У контексті міжплатформних (включаючи windows) сценаріїв або на базі npm-проектів (js чи іншого), ви можете поглянути на модуль крос-env .
Френк Нокк

5
Я сподівався, що одна з відповідей також пояснить, чому це працює лише такий вид, тобто чому це не еквівалентно експорту змінної перед викликом.
Brecht Machiels

4
Чому пояснюється тут: stackoverflow.com/questions/13998075/…
Brecht Machiels

Відповіді:


317
FOO=bar bash -c 'somecommand someargs | somecommand2'

2
Це задовольняє мої критерії (однолінійка, не потребуючи "експорту") ... Я вважаю, що немає способу зробити це без виклику "bash -c" (наприклад, творче використання дужок)?
MartyMacGyver

1
@MartyMacGyver: Нічого, про що я можу придумати. Він також не працюватиме з фігурними дужками.
Призупинено до подальшого повідомлення.

7
Зауважте, що якщо вам потрібно запустити somecommandяк sudo, вам потрібно передати -Eпрапор sudo для передачі змінних. Тому що змінні можуть вводити вразливості. stackoverflow.com/a/8633575/1695680
ThorSummoner

11
Зауважте, що якщо у вашій команді вже є два рівні лапок, то цей метод стає вкрай незадовільним через пекельне цитування. У цій ситуації експорт в нижній частині набагато кращий.
Pushpendre

Ок: на OSX FOO_X=foox bash -c 'echo $FOO_X'працює як слід, але з конкретними назвами вар не виходить: DYLD_X=foox bash -c 'echo $DYLD_X'echos blank. обидві роботи використовують evalзамістьbash -c
mwag

209

Як щодо експорту змінної, але тільки всередині нижньої частини корпусу ?:

(export FOO=bar && somecommand someargs | somecommand2)

У Кіта є пункт, щоб беззастережно виконувати команди, зробіть це:

(export FOO=bar; somecommand someargs | somecommand2)

15
Я б ;скоріше використовував , ніж &&; немає ніякого шляху export FOO=barдо невдачі.
Кіт Томпсон

1
@MartyMacGyver: &&виконує ліву команду, потім виконує праву команду, лише якщо ліва команда вдалася. ;виконує обидві команди беззастережно. Пакетний cmd.exeеквівалент Windows ( ) ;є &.
Кіт Томпсон

2
У zsh мені не здається потрібен експорт для цієї версії: (FOO=XXX ; echo FOO=$FOO) ; echo FOO=$FOOурожайність FOO=XXX\nFOO=\n.
чемпіон

3
@PopePoopinpants: чому б у цьому випадку не використовувати source(ака .)? Крім того, фони вже не слід використовувати в наші дні, і це одна з причин, чому використання $(command)waaaaay безпечніше.
0xC0000022L

3
Так просто, але при цьому так елегантно. І мені подобається ваша відповідь краще, ніж прийнята відповідь, оскільки вона запустить додаткову оболонку, рівну моїй поточній (що може не бути, bashале може бути чимось іншим, наприклад dash), і я не зіткнуся з будь-якими проблемами, якщо потрібно використовувати лапки в межах команд args ( someargs).
Мецьки

45

Ви також можете використовувати eval:

FOO=bar eval 'somecommand someargs | somecommand2'

Оскільки ця відповідь, evalздається, не всім подобається, дозвольте мені щось уточнити: коли використовується як написано, то з цитатами цілком безпечно. Це добре, оскільки він не запустить зовнішній процес (на зразок прийнятої відповіді), а також не буде виконувати команди в додатковій підзаголовку (як і інша відповідь).

Оскільки ми отримуємо декілька регулярних поглядів, можливо, добре дати альтернативу, evalяка сподобається всім, і має всі переваги (і, можливо, навіть більше!) Цього швидкого eval«трюку». Просто використовуйте функцію! Визначте функцію з усіма своїми командами:

mypipe() {
    somecommand someargs | somecommand2
}

і виконайте це зі своїми змінними оточуючого середовища так:

FOO=bar mypipe

7
@Alfe: Ви також спростували прийняту відповідь? тому що він проявляє ті самі "проблеми", що і eval.
gniourf_gniourf

10
@Alfe: на жаль, я не згоден з вашою критикою. Ця команда абсолютно безпечна. Ви справді звучите як хлопець, який колись читав, eval- це зло, не розуміючи, в чому зло eval. І, можливо, ти не дуже розумієш цю відповідь (і насправді в цьому немає нічого поганого). На тому ж рівні: ви б сказали, що lsце погано, бо for file in $(ls)це погано? (і так, ви не спростували прийняту відповідь, і ви також не залишили коментарів). Так часом таке дивне і абсурдне місце.
gniourf_gniourf

7
@Alfe: коли я кажу, що ти справдіevaleval звучиш як хлопець, який колись читав, - це зло, не розуміючи, в чому зле , я маю на увазі ваше речення: у цій відповіді відсутні всі попередження та пояснення, необхідні під час розмови eval. evalне поганий чи небезпечний; не більше bash -c.
gniourf_gniourf

1
Голосуючи в стороні, коментар, наданий @Alfe, якось означає, що прийнята відповідь якось безпечніша. Що було б корисніше, було б вам описати те, що ви вважаєте небезпечним щодо використання eval. У відповіді за умови, що аргументи були цитовані одним захистом від змінних розширень, тому я не бачу проблем з відповіддю.
Бретт Райан

Я усунув свої коментарі, щоб зосередити свою стурбованість в одному новому коментарі: evalце взагалі проблема безпеки (як, bash -cале менш очевидна), тому небезпеку слід згадати у відповіді, що пропонує її використання. Недбайливі користувачі можуть прийняти відповідь ( FOO=bar eval …) і застосувати її до своєї ситуації, щоб вона викликала проблеми. Але відповідачеві, очевидно, було важливіше з'ясувати, чи я спростував його та / або інші відповіді, ніж щось покращувати. Як я писав раніше, справедливість не повинна бути головною турботою; бути не гіршим за будь-яку іншу дану відповідь також беззаперечно.
Альфе

12

Використовуйте env.

Наприклад, env FOO=BAR command. Зауважте, що змінні середовища будуть відновлені / незмінними знову після commandзавершення виконання.

Будьте обережні щодо того, що відбувається заміна оболонки, тобто якщо ви хочете $FOOчітко посилатися на один і той же командний рядок, вам може знадобитися уникнути цього, щоб ваш інтерпретатор оболонки не здійснив заміну до запуску env.

$ export FOO=BAR
$ env FOO=FUBAR bash -c 'echo $FOO'
FUBAR
$ echo $FOO
BAR

-5

Використовуйте сценарій оболонки:

#!/bin/bash
# myscript
FOO=bar
somecommand someargs | somecommand2

> ./myscript

13
Вам ще потрібно export; інакше $FOOбуде змінною оболонки, а не змінною оточення, і тому не видно somecommandабо somecommand2.
Кіт Томпсон

Це спрацювало б, але це перемагає призначення однорядкової команди (я намагаюся навчитися більш творчим способам уникнути багатолінійних та / або скриптів для відносно простих випадків). І що сказав @Keith, хоча принаймні експорт залишився би під сценарій.
MartyMacGyver
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.