Как установить переменную окружения в командной строке и сделать так, чтобы она отображалась в командах?

Вопрос или проблема

Если я выполню

export TEST=foo
echo $TEST

Это выведет foo.

Если я выполню

TEST=foo echo $TEST

Это не сработает. Как я могу получить эту функциональность без использования export или скрипта?

Это происходит потому, что оболочка разворачивает переменную в командной строке до того, как фактически выполняется команда, и в это время переменная не существует. Если вы используете

TEST=foo; echo $TEST

это будет работать.

export заставляет переменную появляться в окружении последовательно выполняемых команд (для того, как это работает в bash, смотрите help export). Если вам нужно, чтобы переменная появилась в окружении одной команды, используйте то, что вы уже пробовали, т.е.

TEST=foo your-application

Синтаксис оболочки описывает это как функционально эквивалентное:

export TEST=foo
your-application
unset TEST

Смотрите спецификацию для деталей.

Интересная часть заключается в том, что команда export переключает флаг экспорта для переменной name. Таким образом, если вы сделаете:

unset TEST
export TEST
TEST="foo"

TEST будет экспортироваться, даже если она не была определена в момент, когда она была экспортирована. Однако дальнейший unset должен удалить атрибут экспорта.

Я подозреваю, что вы хотите, чтобы переменные оболочки имели ограниченную область видимости, а не переменные окружения. Переменные окружения – это список строк, передаваемых командам при их выполнении.

В

var=value echo whatever

вы передаете строку var=value окружению, которое получает echo. Однако echo ничего не делает со своим списком окружения¹, и в любом случае в большинстве оболочек echo встроен и, следовательно, не выполняется.

Если бы вы написали

var=value sh -c 'echo "$var"'

Это было бы другим делом. Здесь мы передаем var=value команде sh, и sh действительно использует свое окружение. Оболочки преобразуют каждую² из полученных переменных из их окружения в переменную оболочки, поэтому переменная окружения var, которую получает sh, будет преобразована в переменную $var, и когда она разворачивается в этой командной строке echo, это станет echo value. Поскольку окружение по умолчанию наследуется, echo также получит var=value в своем окружении (или получил бы, если бы он был выполнен), но, опять же, echo не заботится о окружении.

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

Переносимо (Bourne и POSIX):

(var=value; echo "1: $var"); echo "2: $var"

(…) выше запускает подсистему (новый процесс оболочки в большинстве оболочек), так что любая переменная, объявленная там, будет влиять только на эту подсистему, поэтому я ожидаю, что приведенный выше код выведет “1: value” и “2: ” или “2: whatever-var-was-set-to-before”.

С большинством оболочек, похожих на Bourne (см. Список оболочек, которые поддерживают ключевое слово `local` для определения локальных переменных), вы можете использовать функции и встроенный “local”:

f() {
  local var
  var=value
  echo "1: $var"
}
f
echo "2: $var"

С zsh вы можете использовать анонимные функции, которые, как и обычные функции, могут иметь локальную область видимости:

(){ local var=value; echo "1: $var"; }; echo "2: $var"

или:

function { local var=value; echo "1: $var"; }; echo "2: $var"

С bash и zsh (но не с ash, pdksh или AT&T ksh) этот трюк также работает:

var=value eval 'echo "1: $var"'; echo "2: $var"

Вариант, который работает в нескольких других оболочках (dash, mksh, yash), но не в zsh (если не в эмуляции sh/ksh):

var=value command eval 'echo "1: $var"'; echo "2: $var"

(используя command перед специальной встроенной командой (здесь eval) в оболочках POSIX убирает их особенность (здесь присвоения переменных перед ними остаются в силе после их возврата))

С оболочкой fish вы можете делать переменные локальными для блока begin..end:

begin
  set -l var value
  echo 1: $var
end
echo 2: $var

С mksh (и скоро zsh) вы можете использовать конструкцию ${|cmd}, которая также может иметь локальную область видимости, убедившись, что она не разворачивается в ничего, следя за тем, чтобы вы не устанавливали $REPLY внутри:

${|local var=value; echo "$var"}; echo "$var"

¹ Строго говоря, это не совсем верно. Несколько реализаций будут заботиться о локализационных переменных окружения (LANG, LOCPATH, LC_*…), реализация GNU заботится о переменной окружения POSIXLY_CORRECT (сравните env echo --version с env POSIXLY_CORRECT=1 echo --version на системе GNU).

² что касается только (некоторых из) тех, которые являются допустимыми именами переменных в синтаксисе данной оболочки. Например env ++=foo 1x=bar 3=qwe '#=rty' IFS=asd é=x sh -c 'код оболочки' не создаст ++, 1x, 3, # переменные оболочки, так как это недопустимые имена переменных (хотя # и 3 являются не переменными параметрами оболочки) и не импортирует IFS из окружения, так как это могло бы быть источником уязвимостей в безопасности; что касается é, YMMV.

Вы делаете это правильно, но синтаксис bash легко неправильно интерпретировать: вы могли бы подумать, что echo $TEST заставляет echo извлекать переменную окружения TEST, а затем выводить ее, но это не так. Итак, задано

export TEST=123

тогда

TEST=456 echo $TEST

включает в себя следующую последовательность:

  1. Оболочка разбирает всю командную строку и выполняет все подстановки переменных, так что командная строка превращается в

    TEST=456 echo 123
    
  2. Создаются временные переменные, установленные перед командой, поэтому сохраняется текущее значение TEST и перезаписывается 456; командная строка теперь

    echo 123
    
  3. Выполняется оставшаяся команда, которая в данном случае выводит 123 на стандартный вывод (таким образом, оставшаяся команда оболочки даже не использует временное значение TEST)

  4. Восстанавливается значение TEST

Используйте printenv вместо этого, так как это не включает в себя подстановку переменных:

>> export TEST=123
>> printenv TEST
123
>> TEST=456 printenv TEST
456
>> printenv TEST && TEST=456 printenv TEST && TEST=789 printenv TEST && printenv TEST
123
456
789
123
>>

Вы можете сделать это, используя:

TEST=foo && echo $TEST

Как вы уже обнаружили, в Bash, TEST=456 echo $TEST не работает так, как вы ожидали, потому что вы обращаетесь к переменной в той же строке, которая в настоящее время интерпретируется оболочкой. Но будьте уверены, переменная была установлена на время текущей команды. Чтобы это проверить, вы могли бы сделать что-то вроде этого:

show () { echo "${!1}" ;}
TEST=456 show TEST

Ответ или решение

Чтобы установить переменную окружения в командной строке и использовать её в команде, необходимо учитывать особенности работы оболочки (шелла), такой как Bash. Когда вы пытаетесь выполнить команду следующего вида:

TEST=foo echo $TEST

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

Для того чтобы переменная TEST была доступна в вашей командной строке, вы можете поступить следующим образом:

Установка переменной окружения

  1. Экспорт переменной:
    Используя команду export, вы можете установить переменную окружения, которая будет доступна для последующих команд.

    export TEST=foo
    echo $TEST

    В этом случае вывод будет foo, поскольку переменная была экспортирована и теперь доступна для всех последующих команд в текущем сеансе.

  2. Локальное использование переменной:
    Если вы хотите использовать переменную только в рамках одной команды, вы можете задать её непосредственно перед вызовом нужной команды:

    TEST=foo command

    Это эквивалентно:

    export TEST=foo
    command
    unset TEST

    Важно отметить, что command получает доступ к переменной TEST, но после завершения выполнения команды переменная не сохраняется.

Для проверки значения переменной

Можно использовать различные подходы для проверки значения переменной после её задания:

  • Используйте вызов функции, чтобы получить значение переменной:

    show() { echo "${!1}"; }
    TEST=456 show TEST

Запуск в подкоманде

Если вам нужно ограничить область видимости переменной и не хотите использовать export, можете использовать подкоманду (субшелл):

(var=value; echo "1: $var"); echo "2: $var"

В этом примере первой команде будет доступна только переменная var, созданная в первом блоке. После завершения исполнения подкоманды переменная var не будет доступна во внешнем окружении.

Заключение

При работе с переменными окружения в оболочке Bash важно понимать порядок выполнения команд и подстановки переменных. Ключевые моменты, которые необходимо учитывать:

  • Если хотите, чтобы переменная влияло на последующие команды, используйте export.
  • Для временного использования переменной в рамках одной команды, используйте синтаксис VAR=value command.
  • Поддерживайте чистоту своей среды, освобождая переменные после использования с помощью unset, если они больше не нужны.

Таким образом, правильное понимание и использование переменных в Bash позволяет более эффективно управлять окружением и переменными при работе в терминале.

Оцените материал
Добавить комментарий

Капча загружается...