Установка переменной среды перед командой в Bash не работает для второй команды в конвейере

351

В данной оболочке обычно я устанавливаю переменную или переменные и затем запускаю команду. Недавно я узнал о концепции добавления определения переменной к команде:

FOO=bar somecommand someargs

Это работает ... вроде. Это не работает, когда вы изменяете переменную LC_ * (которая, кажется, влияет на команду, но не на ее аргументы, например, '[az]' char char)) или когда таким образом передается вывод в другую команду:

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

Я также могу добавить к некоторой команде2 "FOO = bar", которая работает, но добавляет нежелательное дублирование и не помогает с аргументами, которые интерпретируются в зависимости от переменной (например, '[az]').

Итак, что хорошего способа сделать это в одной строке?

Я думаю что-то порядка:

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

Я получил много хороших ответов! Цель состоит в том, чтобы сохранить это в одну строку, желательно без использования «экспорта». Метод, использующий вызов Bash, был лучшим в целом, хотя версия в скобках с «экспортом» была немного более компактной. Интересен также способ использования перенаправления, а не канала.

MartyMacGyver
источник
1
(T=$(date) echo $T)будет работать
vp_arth
В контексте кроссплатформенных (в том числе windows) сценариев или проектов на основе npm (js или другое) вы можете взглянуть на модуль cross-env .
Фрэнк
5
Я надеялся, что один из ответов также объяснит, почему это единственный вид работы, то есть почему он не эквивалентен экспорту переменной перед вызовом.
Брехт Machiels
4
Почему это объясняется здесь: stackoverflow.com/questions/13998075/…
Брехт Мачиелс

Ответы:

317
FOO=bar bash -c 'somecommand someargs | somecommand2'
Приостановлено до дальнейшего уведомления.
источник
2
Это удовлетворяет моим критериям (однострочник без необходимости «экспорта») ... Я так понимаю, нет способа сделать это без вызова "bash -c" (например, творческое использование скобок)?
MartyMacGyver
1
@MartyMacGyver: Ни о чем я не могу думать. Это также не будет работать с фигурными скобками.
Приостановлено до дальнейшего уведомления.
7
Обратите внимание, что если вам нужно запустить somecommandкак sudo, вам нужно передать sudo -Eфлаг для передачи через переменные. Потому что переменные могут создавать уязвимости. 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

Как насчет экспорта переменной, но только внутри subshell ?:

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

У Кейта есть смысл безоговорочно выполнять команды:

(export FOO=bar; somecommand someargs | somecommand2)
0xC0000022L
источник
15
Я бы использовал, ;а не &&; нет пути, export FOO=barкоторый потерпит неудачу.
Кит Томпсон
1
@MartyMacGyver: &&выполняет левую команду, затем выполняет правую команду, только если левая команда выполнена успешно. ;выполняет обе команды безоговорочно. cmd.exeЭквивалент Windows batch ( ) ;- это &.
Кит Томпсон
2
В zsh мне не нужен экспорт для этой версии: (FOO=XXX ; echo FOO=$FOO) ; echo FOO=$FOOвыходы FOO=XXX\nFOO=\n.
Rampion
3
@PopePoopinpants: почему бы не использовать source(иначе .) в этом случае? Кроме того, в наши дни не следует больше использовать обратные кавычки, и это одна из причин, по которой использование $(command)становится более безопасным.
0xC0000022L
3
Так просто, но так элегантно. И мне нравится ваш ответ лучше, чем принятый ответ, так как он запустит вложенную оболочку, равную моей текущей (которая может не быть, bashно может быть чем-то другим, например dash), и у меня не возникнет никаких проблем, если я должен использовать кавычки внутри команды args ( someargs).
Меки
45

Вы также можете использовать eval:

FOO=bar eval 'somecommand someargs | somecommand2'

Поскольку этот ответ evalне всем нравится, позвольте мне кое-что прояснить: при использовании в письменном виде с одинарными кавычками он совершенно безопасен. Это хорошо, так как он не будет запускать внешний процесс (например, принятый ответ) и не будет выполнять команды в дополнительной подоболочке (как и другой ответ).

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

mypipe() {
    somecommand someargs | somecommand2
}

и выполните его с переменными вашей среды следующим образом:

FOO=bar mypipe
gniourf_gniourf
источник
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 …) и применить его к своей ситуации, чтобы он вызвал проблемы. Но очевидно, что для ответчика было более важно выяснить, понизил ли я его и / или другие ответы, чем что-либо улучшить. Как я писал ранее, справедливость не должна быть главной заботой; Быть не хуже любого другого данного ответа также безотносительно.
Alfe
12

Использование env.

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

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

$ export FOO=BAR
$ env FOO=FUBAR bash -c 'echo $FOO'
FUBAR
$ echo $FOO
BAR
benjimin
источник
-5

Используйте скрипт оболочки:

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

> ./myscript
Спенсер Рэтбун
источник
13
Вам все еще нужно export; в противном случае $FOOэто будет переменная оболочки, а не переменная окружения, и, следовательно, не видимая для somecommandили somecommand2.
Кит Томпсон
Это бы сработало, но оно побеждает цель иметь однострочную команду (я пытаюсь научиться более креативным способам избежать многострочных и / или сценариев для относительно простых случаев). И то, что сказал @Keith, хотя по крайней мере экспорт останется ограниченным сценарием.
MartyMacGyver