valor temporal del script bash en el comando


11

Como debajo del comando,

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

El libro dice que cuando el comando de asignación de valor ( IFS ":") se usa antes del comando principal ( read a b c d e f <<< "$value"), su valor es efectivo en el comando principal temporalmente. Entonces, el readcomando usa delimitador :.

Pero, como este comando,

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

El mensaje de eco no es hola. ¿Cuál es el verdadero significado del comando anterior?

Respuestas:


5

Esto se reduce a una cuestión de cómo funciona la evaluación. Ambos ejemplos funcionan de la misma manera, el problema ocurre debido a cómo el shell (bash, aquí) expande las variables.

Cuando escribes este comando:

HOME="foo" echo $HOME

El $HOMEse expande antes de ejecutar el comando . Por lo tanto, se expande al valor original y no al nuevo en el que lo configuró para el comando. De hecho, la HOMEvariable se ha cambiado en el entorno en el que echose ejecuta el comando, sin embargo, está imprimiendo $HOMEdesde el padre.

Para ilustrar, considere esto:

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

Como puede ver arriba, el primer comando imprime el valor modificado temporalmente HOMEy el segundo imprime el original, lo que demuestra que la variable solo se modificó temporalmente. Debido a que el bash -c ...comando está encerrado entre comillas simples ( ' ') en lugar de comillas dobles ( " "), la variable no se expande y se pasa como está al nuevo proceso bash. Este nuevo proceso luego lo expande e imprime el nuevo valor en el que se ha establecido. Puede ver que esto suceda si usa set -x:

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

Como puede ver arriba, la variable $HOME nunca se pasa a echo. Solo ve su valor expandido. Comparar con:

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

Aquí, debido a las comillas simples, la variable y no su valor se pasan al nuevo proceso.


7

Cuando el shell analiza una línea, la tokenizará en palabras, realizará varias expansiones (en orden) en las palabras y luego ejecutará el comando.

Suponer test=1:2:3:4:5:6

Veamos este comando: IFS=":" read a b c d e f <<< "$test"

Después de la tokenización, se produce la expansión de parámetros :IFS=":" read a b c d e f <<< "1:2:3:4:5:6"

El shell establecerá la variable IFS para la duración del comando de lectura, y readsabe cómo aplicar $ IFS a su entrada y dar valores a los nombres de las variables.

Este comando tiene una historia similar, pero un resultado diferente: HOME="hello" echo "$HOME"

Como la expansión de parámetros ocurre antes de que comience el comando, el shell tiene:

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

Y luego, durante la ejecución del comando echo, el nuevo valor de $ HOME no se utiliza en absoluto.

Para lograr lo que estás tratando de hacer, elige uno de

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

o

# 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")

pero no elijas el primero.


Probablemente sea mejor elegir el primero. Al menos es significativamente más rápido. Cuando eval es la respuesta, a veces probablemente se esté haciendo la pregunta incorrecta. Pero si alguien debe hacer eso por alguna razón, cambiar la respuesta no hace que la pregunta en sí sea menos errónea. Otra solución es envolverlo en una función y usarlo local.
user23013

¿Por qué se debe evalevitar la solución?
DarkHeart

Si no controla estrictamente la entrada, debe tener mucho cuidado con el código que permite que otras personas inyecten en su programa.
Glenn Jackman

-1

Hay dos ámbitos: variables de entorno y variables locales. Las variables de entorno son válidas para cada proceso (ver setenv, getenv), mientras que las variables locales están activas solo dentro de esta sesión de shell. (No es una distinción obvia ...)

Implícito env(como en su ejemplo) modifica el entorno, mientras echo ...usa los locales, por lo que envno tiene ningún efecto.

Para modificar el uso de variables locales, digamos,

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

Aquí los paréntesis definen el alcance de esta asignación.


1
Esto no tiene nada que ver con el alcance variable, el problema es que la variable se está expandiendo antes de pasar al shell secundario.
terdon
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.