Временное значение bash-скрипта по команде

11

Как команда ниже,

if true; then
   IFS=":" read a b c d e f <<< "$test"

В книге сказано, что когда команда присвоения значения ( IFS ":") используется перед основной командой ( read a b c d e f <<< "$value"), ее значение временно действует для основной команды. Итак, readкоманда использует разделитель :.

Но, как эта команда,

if true; then
   HOME="hello" echo "$HOME"

Эхо-сообщение не привет. Каково истинное значение вышеуказанной команды?

A.Cho
источник

Ответы:

5

Это сводится к вопросу о том, как работает оценка. Оба примера работают одинаково, проблема возникает из-за того, что оболочка (здесь, bash) расширяет переменные.

Когда вы пишете эту команду:

HOME="foo" echo $HOME

$HOMEРасширяется до команды запуска . Следовательно, оно расширяется до исходного значения, а не до нового значения, установленного для команды. HOMEПеременная действительно были изменены в среде , что echoкоманда работает в, однако, вы печатаете $HOMEот родителя.

Для иллюстрации рассмотрим это:

$ HOME="foo" bash -c 'echo $HOME'
foo
$ echo $HOME
/home/terdon

Как вы можете видеть выше, первая команда печатает временно измененное значение, HOMEа вторая печатает оригинал, демонстрируя, что переменная была изменена только временно. Поскольку bash -c ...команда заключена в одинарные кавычки ( ' ') вместо двойных ( " "), переменная не раскрывается и передается как есть новому процессу bash. Этот новый процесс затем расширяет его и печатает новое значение, для которого он был установлен. Вы можете увидеть это, если вы используете set -x:

$ set -x
$ HOME="hello" echo "$HOME"
+ HOME=hello         
+ echo hello
hello

Как вы можете видеть выше, переменная $HOME никогда не передается echo. Он видит только его расширенное значение. Сравнить с:

$ HOME="hello" bash -c 'echo $HOME'
+ HOME=hello
+ bash -c 'echo $HOME'
hello

Здесь из-за одинарных кавычек переменная, а не ее значение передаются новому процессу.

Тердон
источник
7

Когда оболочка разбирает строку, она разбивает строку на слова, выполняет различные расширения (по порядку) над словами, а затем выполняет команду.

предполагать test=1:2:3:4:5:6

Давайте посмотрим на эту команду: IFS=":" read a b c d e f <<< "$test"

После токенизации и расширения параметров происходит:IFS=":" read a b c d e f <<< "1:2:3:4:5:6"

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

Эта команда имеет похожую историю, но другой результат: HOME="hello" echo "$HOME"

Поскольку расширение параметра происходит до начала команды, оболочка имеет:

HOME="hello" echo "/home/username"

И затем, во время выполнения команды echo, новое значение $ HOME вообще не используется.

Чтобы добиться того, что вы пытаетесь сделать, выберите один из

# Delay expansion of the variable until its new value is set
HOME="hello" eval 'echo "$HOME"'

или

# Using a subshell, so the altered env variable does not affect the parent.
# The semicolon means that the variable assignment will occur before
# the variable expansion
(HOME="hello"; echo "$HOME")

но не выбирай первый.

Гленн Джекман
источник
Вероятно, лучше выбрать первый. По крайней мере, это значительно быстрее. Когда ответом является eval, вы, вероятно, иногда задаете не тот вопрос. Но если кто-то должен сделать это по каким-то причинам, изменение ответа не делает сам вопрос менее ошибочным. Другое решение заключается в том, чтобы обернуть его в функцию и использовать local.
user23013
Почему следует evalизбегать решения?
DarkHeart
Если вы не строго контролируете ввод, вы должны быть очень осторожны с кодом, который вы позволяете другим людям внедрять в вашу программу.
Гленн Джекман
-1

Есть две области: переменные окружения и локальные переменные. Переменные среды действительны для каждого процесса (см. setenv, getenv), В то время как локальные переменные активны только в этом сеансе оболочки. (Это не очевидное различие ...)

Подразумевается env(как в вашем примере) изменяет среду, в то время как echo ...использует локальные - так envчто не имеет никакого эффекта.

Для изменения локальных переменных используйте, скажем,

( HOME="foo" ; echo "$HOME" )

Здесь круглые скобки определяют объем этого назначения.

Андрей Милорадовский
источник
1
Это не имеет ничего общего с областью действия переменной, проблема в том, что переменная раскрывается до ее передачи в дочернюю оболочку.
Terdon