¿Cómo se emparejan (emparejan) las comillas dobles en bash?


15

Estoy usando GNU bash 4.3.48. Considere los siguientes dos comandos que solo difieren en un solo signo de dólar.

Comando 1:

echo "(echo " * ")"

Comando 2:

echo "$(echo " * ")"

La salida de ellos es, respectivamente

(echo  test.txt ppcg.sh )

y

 * 

Así que, obviamente, en el primer caso, el *globo está englobado, lo que significa que la primera comilla va con el segundo para formar un par, y el tercero y el cuarto forman otro par.

En el segundo caso, el *no está globalizado, y hay exactamente dos espacios adicionales en la salida, uno antes del asterisco y otro después, lo que significa que la segunda comilla va con la tercera y la primera va con el cuarto.

¿Hay otros casos además de la $()construcción en los que las comillas no coinciden con la siguiente, sino que están anidadas? ¿Está bien documentado este comportamiento? En caso afirmativo, ¿dónde puedo encontrar el documento correspondiente?



Una vez más, el resaltado de sintaxis en UL SE es incorrecto y engañoso, aunque prettify JS es el culpable.
Weijun Zhou

Respuestas:


14

Cualquiera de las construcciones de anidamiento que se pueden interpolar dentro de las cadenas puede tener más cadenas dentro de ellas: se analizan como un nuevo script, hasta el marcador de cierre, e incluso se pueden anidar en varios niveles de profundidad. Todos menos uno de esos comienzan con a $. Todos ellos están documentados en una combinación del manual de Bash y la especificación del lenguaje de comandos de shell POSIX.

Hay algunos casos de estas construcciones:

  • Sustitución de comandos con$( ... ) , como has encontrado. POSIX especifica este comportamiento :

    Con el $(command)formulario, todos los caracteres que siguen el paréntesis abierto al paréntesis de cierre coincidente constituyen el comando. Se puede usar cualquier script de shell válido para el comando ...

    Las citas son parte de los scripts de shell válidos, por lo que se permiten con su significado normal.

  • Sustitución de comandos usando `, también.
  • El elemento "palabra" de instancias de sustitución de parámetros avanzados como${parameter:-word} . La definición de "palabra" es :

    Una secuencia de caracteres tratados como una unidad por el shell

    , que incluye texto citado e incluso citas mixtas a"b"c'd'e, aunque el comportamiento real de las expansiones es un poco más liberal que eso, y por ejemplo ${x:-hello world}también funciona.

  • La expansión aritmética con $(( ... )), aunque es en gran medida inútil allí (pero también puede anidar la sustitución de comandos o expansiones variables, y luego tener comillas útiles dentro de ellas). POSIX afirma que :

    La expresión se tratará como si estuviera entre comillas dobles, excepto que una comilla doble dentro de la expresión no se trata especialmente. El shell expandirá todos los tokens en la expresión para expansión de parámetros, sustitución de comandos y eliminación de comillas.

    entonces este comportamiento es explícitamente requerido. Eso significa echo "abc $((4 "*" 5))"aritmética, en lugar de globbing.

    Sin embargo, $[ ... ]tenga en cuenta que la expansión aritmética de estilo antiguo no se trata de la misma manera: las comillas serán un error si aparecen, independientemente de si la expansión se cita o no. Este formulario ya no está documentado, y no está destinado a ser utilizado de todos modos.

  • Traducción específica de la configuración regional$"..." , que en realidad usa el "como elemento central. $"se trata como una sola unidad

Hay otro caso de anidación que no puede esperar, que no involucra comillas, que es con expansión de llaves : se {a,b{c,d},e}expande a "a bc bd e". ${x:-a{b,c}d}hace no nido, sin embargo; se trata como una sustitución de parámetro que da " a{b,c", seguido de " d}". Eso también está documentado :

Cuando se usan llaves, la llave final coincidente es el primer '}' que no se escapa por una barra invertida o dentro de una cadena entre comillas, y no dentro de una expansión aritmética incorporada, sustitución de comando o expansión de parámetros.


Como regla general, todas las construcciones delimitadas analizan sus cuerpos independientemente del contexto circundante (y las excepciones se tratan como errores ). En esencia, al ver $(el código de sustitución de comandos solo le pide al analizador que consuma lo que pueda del cuerpo como si fuera un nuevo programa, y ​​luego verifica que el marcador de terminación esperado (sin escape )o ))o }) aparezca una vez que se ejecuta el sub-analizador fuera de las cosas que puede consumir.

Si piensa en el funcionamiento de un analizador de descenso recursivo , eso es solo una simple recurrencia al caso base. En realidad, es más fácil de hacer que a la inversa, una vez que tienes la interpolación de cadenas. Independientemente de la técnica de análisis subyacente, los depósitos que soportan estas construcciones dan el mismo resultado.

Puede anidar las citas tan profundamente como desee a través de estas construcciones y funcionará como se espera. Ningún lugar se confundirá al ver una cita en el medio; en cambio, ese será el comienzo de una nueva cadena entre comillas en el contexto interior.


Gracias. En "blah/blah\n$(cat "${tmpdir}/${filename}.jpdf")", ¿por qué la segunda comilla doble no es el final de la primera comilla doble (como lo muestra el resaltado de sintaxis en su respuesta), sino un comienzo de una cadena dentro $(...)? ¿Es porque el analizador de bash es de arriba hacia abajo, en lugar de de abajo hacia arriba?
StackExchange para todos

2
Hay mucha variación en el manejo de "${var-"foo"}"( echo "${-+"*"}"es el mismo que echo *en el shell Bourne o Korn, por ejemplo) y el comportamiento se hará claramente no especificado en la próxima versión del estándar . Vea también la discusión en mail-archive.com/austin-group-l@opengroup.org/msg00167.html
Stéphane Chazelas

3

Quizás mirar los dos ejemplos con printf(en lugar de echo) ayude:

$ printf '<%s> ' "(echo " * ")"; echo
<(echo > <test.txt> <ppcg.sh> <file1> <file2> <file3> <)>

Imprime (echo (la primera palabra, incluido un espacio final), algunos archivos y la palabra de cierre ).
El paréntesis es solo parte de la cadena citada (echo .
El asterisco (ahora sin comillas, ya que las dos comillas dobles están emparejadas) se expande como un globo a una lista de archivos coincidentes.
Y luego, el paréntesis de cierre.

Sin embargo, su segundo comando funciona de la siguiente manera:

$ printf '<%s> ' "$(echo " * ")" ; echo
< * >

El $inicia una sustitución de comando. Eso comienza de nuevo la cita.
Se cita el asterisco " * "y eso es lo que echogenera el comando (aquí es un comando y no una cadena entre comillas) . Finalmente, printfvuelve a formatear el *e imprime como < * >.

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.