comillas dobles anidadas en una sola línea altamente votada

20

Una respuesta de StackOverflow con> 3.5K votos presenta esta línea para asignar al DIRdirectorio del script bash actual:

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

Estoy desconcertado por las comillas dobles anidadas. Por lo que puedo decir, los siguientes fragmentos están entre comillas dobles:

"$( cd "
"${BASH_SOURCE[0]}"
" && pwd )"

... y todo lo demás a la derecha de =(es decir, $( dirnamey )) no está entre comillas. En otras palabras, supongo que los caracteres segundo, cuarto y sexto ""cierran" los caracteres primero, tercero y quinto ", respectivamente.

Entiendo lo que "${BASH_SOURCE[0]}"logran las comillas dobles , pero ¿cuál es el propósito de los otros dos pares de comillas dobles?

Si, por otro lado (y a pesar del alto puntaje de voto), el fragmento anterior es incorrecto, ¿cuál es la forma correcta de lograr su intención nominal?

(Por intención nominal quiero decir: recopilar el valor devuelto por pwddespués de primero cden el directorio devuelto por dirname "${BASH_SOURCE[0]}", y hacer el cd-ing en un sub-shell, para que el $PWDdel shell principal permanezca sin cambios).

kjo
fuente
1
que es debido a una característica de $ (...): $( here, it's a subshell, but you are writing code as if you were writing it on the "first level" of the shell .... ).
Olivier Dulac
Vine aquí por el script de instalación de Docker. Para encontrar el nombre de la distribución:lsb_dist="$(. /etc/os-release && echo "$ID")"; echo "$lsb_dist"
David Tonhofer
Tenga en cuenta que no se necesitan espacios en la línea: también DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"funciona.
David Tonhofer

Respuestas:

12

Su acertijo no es correcto acerca de cómo bash(y el shell en general) analizó la entrada. En:

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

Primero, bashanalice el lado derecho de la asignación a una cadena larga $( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )porque la comilla doble puede aparecer dentro de las comillas dobles .

Después de eso, bashcomience a analizar la sustitución del comando. Debido a que todos los caracteres que siguen al paréntesis abierto al paréntesis adjunto se usan para construir el comando dentro de la sustitución del comando, obtendrá:

cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd

El shell continúa analizando ese comando compuesto, divídalo en dos partes:

  • cd "$( dirname "${BASH_SOURCE[0]}" )"
  • pwd

Luego, aplica la misma regla de análisis cd "$( dirname "${BASH_SOURCE[0]}" )", pero esta vez, las comillas dobles no son redundantes, pero tienen sentido. Evitan la división de campos en función del resultado $( dirname "${BASH_SOURCE[0]}" )y también la expansión de ${BASH_SOURCE[0]}(En contraste con las comillas dobles más externas, no será necesario en RHS de asignación de variables para prevenirsplit+glob ).


Esta regla se aplica a la sustitución de comandos en todos los shell POSIX . Puede encontrar un rompecabezas más detallado en la sección de Reconocimiento de tokens de la especificación POSIX .

Cuonglm
fuente
21

Una vez que uno está adentro $(...), las citas comienzan desde cero.

En otras palabras, "..."y $(...)pueden anidar uno dentro del otro. La sustitución de procesos $(...), puede contener una o más cadenas completas entre comillas dobles. Además, las cadenas entre comillas dobles pueden contener una o más sustituciones de proceso completas . Pero no se entrelazan. Por lo tanto, una cadena de comillas dobles que comienza dentro de una sustitución de proceso nunca se extenderá fuera de ella o viceversa.

Entonces, considere:

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

Dentro del interior $(...)está:

dirname "${BASH_SOURCE[0]}"

En lo anterior ${BASH_SOURCE[0]}se cita doblemente. Cualquier comilla, doble o simple, fuera de la, $(...)es irrelevante al determinar que ${BASH_SOURCE[0]}se cita entre comillas dobles.

El exterior $(...)contiene:

cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd

Aquí, la expresión $( dirname "${BASH_SOURCE[0]}" )está entre comillas dobles. El hecho de que haya citas fuera del exterior $(...)es irrelevante cuando se considera lo que está dentro de él. El hecho de que haya citas dentro de lo interno $(...)también es irrelevante.

Así es como coinciden las comillas dobles:

ingrese la descripción de la imagen aquí

John1024
fuente
¿Qué quiere decir irrelevant? Excepto por el paréntesis más externo, todos los demás tienen su propio significado.
Cuonglm
44
Y, esta es la razón por la que no debe usar backticks: las reglas de comillas son extremadamente extrañas y no intuitivas.
Comodín el
La oración "se $(...)une más fuerte que "..."" no tiene sentido. No son operadores infijos, y no hay jerarquía entre ellos (si lo hubiera, significaría que las comillas no pueden estar entre paréntesis o los paréntesis no pueden estar entre comillas, pero ese no es el caso). El término técnico es eso $(…)y "…"nido.
Gilles 'SO- deja de ser malvado'
@Gilles Muy bien. Acabo de volver a redactar con la esperanza de capturar mejor el concepto.
John1024