En esta pregunta, alguien informa un problema al usar un documento aquí con una palabra delimitadora citada dentro de la $(...)
sustitución de comando , donde una barra invertida \
al final de una línea dentro del documento desencadena la continuación de la línea de unión de nueva línea , mientras que el mismo documento aquí fuera de la sustitución de comando funciona como se esperaba .
Aquí hay un documento de ejemplo simplificado:
cat <<'EOT'
abc ` def
ghi \
jkl
EOT
Esto incluye una barra de retroceso y una barra invertida al final de una línea. Se cita el delimitador, por lo que no se producen expansiones dentro del cuerpo. En todos los Bourne-alikes puedo encontrar esta salida de los contenidos textualmente. Si pongo el mismo documento dentro de una sustitución de comando de la siguiente manera:
x=$(cat <<'EOT'
abc ` def
ghi \
jkl
EOT
)
echo "$x"
entonces ya no se comportan de manera idéntica:
dash
,ash
,zsh
,ksh93
, BusyBoxash
,mksh
y SunOS 5.10 POSIXsh
todos dan los contenidos literales del documento, como antes.- Bash 3.2 da un error de sintaxis para un backtick sin igual. Con backticks coincidentes, intenta ejecutar el contenido como un comando.
- Bash 4.3 colapsa "ghi" y "jkl" en una sola línea, pero no tiene ningún error. La
--posix
opción no afecta esto. Kusalananda me dice (¡gracias!) Que sepdksh
comporta de la misma manera .
En la pregunta original, dije que esto era un error en el analizador de Bash. ¿Lo es? [Actualización: sí ] El texto relevante de POSIX (todo de la definición del lenguaje de comandos de Shell) que puedo encontrar es:
- §2.6.3 Sustitución de comandos :
Con el formulario $ (comando), 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 , excepto un script que consista únicamente en redirecciones que produzcan resultados no especificados.
- §2.7.4 Aquí-Documento :
Si se cita una parte de la palabra , el delimitador se formará mediante la eliminación de la cita en la palabra , y las líneas del documento aquí no se expandirán.
- §2.2.1 Carácter de escape (barra invertida) :
Si un <newline> sigue al <backslash>, el shell interpretará esto como una continuación de línea. Las <backslash> y <newline> se eliminarán antes de dividir la entrada en tokens.
- §2.3 Reconocimiento de tokens :
Cuando la gramática ha reconocido un token io_here (ver Gramática de Shell ), una o más de las líneas posteriores que siguen inmediatamente al siguiente token NEWLINE forman el cuerpo de uno o más documentos aquí y se analizarán de acuerdo con las reglas de Aquí. Documento .
Cuando no está procesando un io_here , el shell dividirá su entrada en tokens aplicando la primera regla aplicable a continuación al siguiente carácter en su entrada. ...
...
- Si el carácter actual es <backslash>, comillas simples o comillas dobles y no se cita, afectará la cita para los caracteres posteriores hasta el final del texto citado. Las reglas para las citas son como se describen en las citas . Durante el reconocimiento de tokens, no se realizarán sustituciones, y el token resultante contendrá exactamente los caracteres que aparecen en la entrada (excepto para la unión de <línea nueva>), sin modificar, incluidas las comillas o operadores de sustitución incluidos o incluidos, entre el final y el final. del texto citado.
Mi interpretación de esto es que todos los caracteres posteriores $(
hasta la terminación )
comprenden el script de shell, textualmente; aparece un documento aquí, por lo que el procesamiento del documento aquí ocurre en lugar de la tokenización ordinaria; el documento aquí tiene un delimitador citado, lo que significa que su contenido se procesa textualmente; y el personaje de escape nunca entra en juego. Sin embargo, puedo ver un argumento de que este caso simplemente no se aborda y que ambos comportamientos son permisibles. Es posible que también haya omitido algún texto relevante en alguna parte.
- ¿Esta situación se aclara en otra parte?
- ¿En qué debería poder confiar un script portátil (en teoría)?
- ¿El tratamiento específico de alguno de estos shells (Bash 3.2 / Bash 4.3 / todos los demás) es obligatorio según el estándar? ¿Prohibido? ¿Permitido?
echo "$x"
, pero cualquier forma de inspeccionar la variable funciona. He editado esa línea en la parte inferior.
$(...)
por lo que sea esa salida ... Ahora, cuando ejecuta el comando en su ejemplo en una subshell (en bash
) sí genera el resultado esperado. Es solo cuando lo convierte en sustitución de comando que colapsa "ghi" y "jkl". Así que esto es un error de la OMI