variable expansions bash heredoc expansion quoting

expansions - Expansión de la variable bash ${var:+“…”} en este documento ¿eliminando las comillas dobles?



if bash (3)

El comportamiento parece deliberado: es consistente en todos los shells Bourne que probé (por ejemplo, ksh93 y zsh se comportan de la misma manera).

El comportamiento es equivalente a tratar el documento aquí como doble citado solo para estas expansiones especiales . En otras palabras, obtienes el mismo resultado para

$ echo "${var:+"hi there"}" hi there $ echo "${var:+''Bye''}" ''Bye''

Solo hay una pista muy débil en la especificación POSIX. Encontré que algo especial sucede con las palabras entre comillas dobles en las expansiones de parámetros. Esto es de la sección informativa "Ejemplos" de la expansión de parámetros :

La doble cita de los patrones es diferente dependiendo de dónde se colocan las comillas dobles.

"${x#*}"
El <asterisk> es un carácter de patrón.
${x#"*"}
El literal <asterisk> está citado y no es especial.

Leería la última línea como sugerir que la eliminación de comillas para comillas dobles se aplica a la palabra. Este ejemplo no tendría sentido para las comillas simples, y por omisión, no hay eliminación de comillas para las comillas simples.

Actualizar

Probé el FreeBSD /bin/sh , que se deriva de un Shell de Almquist. Este shell produce comillas simples y dobles. Por lo tanto, el comportamiento ya no es consistente en todos los shells, solo en la mayoría de los shells que probé.

En cuanto a obtener comillas dobles en la expansión de la palabra después de :+ , mi opinión es

$ var=1 $ q=''"'' $ cat <<EOF ${var:+${q}hi there$q} EOF "hi there"

Estoy tratando de entender por qué Bash elimina las comillas dobles (pero no comillas simples) al realizar la expansión variable con ${parameter:+word} ( Usar valor alternativo ), en un documento aquí, por ejemplo:

% var=1 % cat <<EOF > ${var:+"Hi there"} > ${var:+''Bye''} > EOF Hi there ''Bye''

Según el manual, la "palabra" después de :+ se procesa con expansión de tilde, expansión de parámetros, sustitución de comandos y expansión aritmética. Ninguno de estos debe hacer nada.

¿Qué me estoy perdiendo? ¿Cómo puedo obtener comillas dobles en la expansión?


tl; dr

$ var=1; cat <<EOF "${var:+Hi there}" ${var:+$(printf %s ''"Hi there"'')} EOF "Hi there" "Hi there"

Dos soluciones pragmáticas para incluir comillas dobles en el valor alternativo.
El enfoque de $(...) incrustado $(...) es más engorroso, pero más flexible: permite la inclusión de comillas dobles incrustadas y también le permite controlar si el valor debe ampliarse o no.

La útil respuesta de Jens y la útil respuesta de Patryk Obara arrojaron luz y demostraron aún más el problema.

Tenga en cuenta que el comportamiento problemático se aplica igualmente a :

  • (como se indica en las otras respuestas) cadenas regulares entre comillas dobles (por ejemplo, echo "${var:+"Hi there"}" ; para la primera solución, tendrías que -citar las instancias que la rodean " ; por ejemplo, echo "/"${var:+Hi there}/"" ; curiosamente, como señala Gunstick en un comentario sobre la pregunta, usar /" en el valor alternativo para producir " en la salida funciona en cadenas entre comillas dobles, por ejemplo, , echo "${var:+/"Hi th/"ere/"}" - a diferencia de en las citas aquí-docs.)

  • expansiones relacionadas ${var+...} , ${var-...} / ${var:-...} , y ${var=...} / ${var:=...}

  • Además, hay una rareza relacionada con el manejo de / dentro de los valores alternativos de comillas dobles dentro de una cadena de comillas dobles / sin comillas aquí: doc: bash y ksh eliminan inesperadamente las instancias incrustadas; p.ej,
    echo "${nosuch:-"a/b"}" produce de forma inesperada ab , aunque echo "a/b" en aislamiento produce a/b - vea esta pregunta .

No tengo una explicación para el comportamiento [1] , pero puedo ofrecer soluciones pragmáticas que funcionan con todos los principales shells compatibles con POSIX ( dash , bash , ksh , zsh ):

Tenga en cuenta que " instancias nunca son necesarias por razones sintácticas dentro del valor alternativo: el valor alternativo se trata implícitamente como una cadena entre comillas dobles : no hay expansión de tilde, no hay división de palabras y no se realiza el intercambio de palabras, pero las expansiones de parámetros, las expansiones aritméticas y Se realizan sustituciones de comando.

Tenga en cuenta que en las expansiones de parámetros que incluyen sustitución o prefijo / eliminación de sufijo , las comillas tienen un significado sintáctico; por ejemplo: echo "${BASH#*"bin"}" o echo "${BASH#*''bin''}" - curiosamente, el dash no admite comillas simples, sin embargo.

  • Si desea rodear todo el valor alternativo con comillas , y no tiene comillas incrustadas y desea expandirlo ,
    cite la expansión completa , lo que evita el problema de " eliminación del valor alternativo:

    # Double quotes $ var=1; cat <<EOF "${var:+The closest * is far from $HOME}" EOF "The closest * is far from /Users/jdoe"

    # Single quotes - but note that the alternative value is STILL EXPANDED, # because of the overall context of the unquoted here-doc. var=1; cat <<EOF ''${var:+The closest * is far from $HOME}'' EOF ''The closest * is far from /Users/jdoe''

  • Para cotizaciones incrustadas , o para evitar la expansión del valor alternativo ,
    use una sustitución de comando incrustada (sin comillas, aunque se comportará como si estuviera citado):

    # Expanded value with embedded quotes. var=1; cat <<EOF ${var:+$(printf %s "We got 3/" of snow at $HOME")} EOF We got 3" of snow at /Users/jdoe

    # Literal value with embedded quotes. var=1; cat <<EOF ${var:+$(printf %s ''We got 3" of snow at $HOME'')} EOF We got 3" of snow at $HOME

Estos dos enfoques se pueden combinar según sea necesario.

[1] En efecto, el valor alternativo:

  • se comporta como una cadena implícitamente entre comillas dobles,
  • '' instancias, como en las cadenas de comillas dobles, se tratan como literales .

Dado lo anterior,

  • Tendría sentido tratar las " instancias incrustadas como literales también, y simplemente pasarlas a través, como las '' instancias.
    En cambio, lamentablemente, se eliminan y, si intenta escapar de ellos como /" , también se retiene / (dentro de los documentos no citados aquí, pero, curiosamente, no dentro de cadenas entre comillas dobles), excepto en ksh (la excepción loable) , donde se eliminan las / instancias. En zsh , curiosamente, tratar de usar /" rompe la expansión por completo (como lo hacen los no salvados no escapados en todas las capas).

    • Más específicamente, las comillas dobles no tienen una función sintáctica en el valor alternativo, pero se analizan como si lo hicieran : se aplicó la eliminación de comillas y no se pueden usar instancias (no balanceadas) en el interior sin que /" escapen ( lo cual, como se dijo, es inútil, porque las / instancias son retenidas).

Dada la semántica implícita de cadena de doble comilla, las instancias literales de $ deben ser /$ -escaped, o se debe usar una sustitución de comando para incrustar una cadena de comilla simple ( $(printf %s ''...'') ).


$ cat <<EOF ${var:+bare alt value is string already} ${var:+''and these are quotes within string''} ${var:+"these are double quotes within string"} ${var:+"which are removed during substitution"} "${var:+but you can simply not substitute them away ;)}" EOF bare alt value is string already ''and these are quotes within string'' these are double quotes within string which are removed during substitution "but you can simply not substitute them away ;)"

Tenga en cuenta que el documento aquí no es necesario para reproducir esto:

$ echo "${var:+''foo''}" ''foo''