Как печатать/изменять переменные среды?

Как распечатать только что настроенную переменную среды?

NAME=sam echo "$NAME" # empty

Вы можете увидеть здесь, используя eval, он работает. Так ли это?

NAME=sam eval 'echo $NAME' # => sam

Ответ 1

Они должны идти как разные команды, например:

NAME=sam; echo "$NAME"
NAME=sam && echo "$NAME"

Расширение $NAME в пустую строку выполняется оболочкой раньше, перед запуском echo, поэтому в то время, когда переменная NAME передается в среду команд командной строки echo, расширение уже выполнено (для null string).

Чтобы получить тот же результат в одной команде:

NAME=sam printenv NAME

Ответ 2

Чтобы привести существующие ответы вместе с важным разъяснением:

Как уже говорилось, проблема с NAME=sam echo "$NAME" заключается в том, что $NAME расширяется текущей оболочкой до того, как назначение NAME=sam вступит в силу.

Решения, которые сохраняют исходную семантику ((неэффективной) попытки решения NAME=sam echo "$NAME"):

Используйте либо eval [1] (как в самом вопросе), либо printenv (как добавлено Аароном МакДейдом к ответу на вопрос), либо bash -c (из ответа Ljm Dullaart) в порядке убывания эффективности:

NAME=sam eval 'echo "$NAME"'  # use 'eval' only if you fully control the command string
NAME=sam printenv NAME
NAME=sam bash -c 'echo "$NAME"'

printenv - это не утилита POSIX, но она доступна как для Linux, так и для macOS/BSD.

Что этот стиль вызова (<var>=<name> cmd...) делает для определения NAME:

  • как переменная среды
  • это определено только для вызываемой команды.

Другими словами: NAME существует только для вызываемой команды и не влияет на текущую оболочку (если до этого не существовало никакой переменной с именем NAME после не будет ни одной; существующая переменная NAME остается неизменной).

POSIX определяет правила для такого рода вызовов в главе " Поиск и выполнение команд".


Следующие решения работают совсем по-другому (от ответа heemayl):

NAME=sam; echo "$NAME"
NAME=sam && echo "$NAME"

Хотя они выдают одинаковый результат, они вместо этого определяют:

  • переменная оболочки NAME (только), а не переменная окружения
    • если бы echo была командой, использующей переменную окружения NAME, она не была бы определена (или потенциально определена иначе, чем ранее).
  • который живет после команды.

Обратите внимание, что каждая переменная среды также отображается как переменная оболочки, но обратное неверно: переменные оболочки видны только текущей оболочке и ее подоболочкам, но не дочерним процессам, таким как внешние утилиты и (неисследованные) сценарии. (если только они не помечены как переменные окружения с помощью export или declare -x).


[1] Технически, bash здесь нарушает POSIX (как и zsh): поскольку eval - это специальная встроенная оболочка, предыдущее назначение NAME=sam должно привести к тому, что переменная $NAME останется в области действия после завершения команды, но это не то, что происходит.
Однако, когда вы запускаете bash в режиме совместимости с POSIX, он совместим.
dash и ksh всегда соответствуют.
Точные правила сложны, и некоторые аспекты оставлены на усмотрение реализаций;снова см. Поиск и выполнение команд.
Кроме того, применяется обычный отказ от ответственности: используйте eval только в тех случаях, когда вы полностью контролируете или неявно доверяете.

Ответ 3

Это тоже работает с помощью двоеточия.

NAME=sam; echo $NAME

Ответ 4

Синтаксис

variable=value command

часто используется для установки переменных среды для определенного процесса. Однако вы должны понять, какой процесс получает переменную и кто ее интерпретирует. В качестве примера, используя две оболочки:

a=5
# variable expansion by the current shell:
a=3 bash -c "echo $a"
# variable expansion by the second shell:
a=3 bash -c 'echo $a'

Результат будет 5 для первого эха и 3 для второго.