
Estoy tratando de comprender las expansiones de shell en bash (GNU bash, versión 4.4.20(1) -lanzamiento (i686-pc-linux-gnu)).
Escribiendo en mi shell bash interactivo
x='$(id)'
$x
$(echo $x)
Esperaba de cualquiera de las últimas 2 líneas un error del formulario
bash: uid=xxx(user): command not found
pero tengo
bash: $(id): command not found
.
No entiendo por qué la sustitución de comandos no ocurre aquí. ¿No debería realizarse después de una expansión variable? Supongo que tiene que ver con las operaciones de Shell como se describen aquí.https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Shell-Operation
¿Alguien puede explicar este comportamiento?
Solo estoy interesado en comprender con mayor precisión las expansiones de bash. No estoy interesado en ejecutar un script real en mi pregunta.
Respuesta1
$(…)
es una sustitución de comando (“sustitución de proceso” es <(…)
y similares). Las sustituciones de variables y las sustituciones de comandos ocurren en el mismo paso, de izquierda a derecha en la cadena. Lo único que ocurre como resultado de estas sustituciones es la división de palabras y la agrupación.
Entonces x='$(id)'
se establece x
en la cadena de 5 caracteres $(id)
. Luego, para ejecutar $x
, el shell lo reemplaza $x
por el valor $(id)
. No contiene ningún espacio en blanco ni carácter global, por lo que se trata como un nombre de comando.
Contrastar con:
x='@(id)'
shopt -s extglob
echo /none/$x /usr/bin/$x
Suponiendo que el archivo /none/id
no existe pero /usr/bin/id
existe, el echo
comando se expande a tres palabras: echo
(obviamente), /none/@(id)
(el patrón global /none/@(id)
no coincide con nada, por lo que no se modifica) y /usr/bin/id
(el patrón global /usr/bin/@(id)
coincide con un archivo, por lo que se reemplaza por la lista de coincidencias de un elemento).
En el manual de bash, la oración relevante está al comienzo delExpansiones de caparazónsección.
El orden de las expansiones es: expansión de llaves; expansión de tilde, expansión de parámetros y variables, expansión aritmética y sustitución de comandos (realizada de izquierda a derecha); división de palabras; y expansión del nombre de archivo.
Todo lo que esté entre dos puntos y coma es una pasada. Cada pase funciona sobre el resultado del pase anterior.
Tenga en cuenta que una sola frase (incluso una compleja como la que cité anteriormente) no puede contar toda la historia. La semántica de Shell es complicada. Dudo que el manual de cualquier shell tenga los detalles de todos los casos de esquina. Elespecificación POSIXes más formal pero no cubre extensiones específicas de bash e incluso deja sin definir algunos casos realmente extraños.
Respuesta2
Es la cita. Las comillas simples ( '
) definen una cadena literal y no se puede producir interpolación ni escape. Las comillas dobles ( "
) permiten la interpolación y el escape.
He aquí un ejemplo:
$ x='$(id)'
$ echo 'The variable x contains the value \"$x\"'
The variable x contains the value \"$x\"
$ echo "The variable x contains the value \"$x\""
The variable x contains the value "$(id)"
$ x="$(id)"
$ echo "The variable x contains the value \"$x\""
The variable x contains the value "uid=1000(myusername)..."
Aquí hay otro ejemplo de interpolación y escape basado en comillas simples versus comillas dobles:
$ echo 'The current directory is $PWD according to the variable \"\$PWD\".'
The current directory is $PWD according to the variable \"\$PWD\".
$ echo "The current directory is $PWD according to the variable \"\$PWD\"."
The current directory is /tmp according to the variable "$PWD".