¿Necesito citar sustituciones de comandos al asignar su salida a una variable?

¿Necesito citar sustituciones de comandos al asignar su salida a una variable?

Tiendo a citar sustituciones de comandos como se muestra a continuación, incluso cuando asigno su salida a una variable:

var="$(command)"

¿Es eso realmente necesario? ¿Cuándo se rompe? La respuesta aceptadaaquíreclamos:

DIRNAME="$(dirname $FILE)"no hará lo que quierassi $FILE contiene espacios en blanco o caracteres globales [?*.

El enlace apunta a la excelente página de Gray Cat Wiki sobre citas, pero esa página no menciona específicamente las sustituciones de comandos entre citas. Y mientras cita elvariablees claramente necesario, citar la sustitución del comando en sí no parece serlo.

Sin embargo, la misma publicación concluye con:

DIRNAME="$(dirname "$FILE")" es la forma recomendada. Puede reemplazar DIRNAME= con un comando y un espacio sin cambiar nada más, y dirname recibe la cadena correcta.

Que es lo que siempre he pensado y, a menudo, he corregido publicaciones aquí que no lo citaban. Sin embargo, la página wiki vinculada anteriormente también afirma que:

Hay algunos casos en los que se pueden omitir las comillas dobles de forma segura:

En el lado derecho de una tarea sencilla. Puedes escribir foo=$bar sin comillas. Esto es compatible con POSIX.

[. . . ]

Si bien var=$(command)no es realmente una tarea "simple", no pude encontrar un caso en el que las citas fueran realmente necesarias:

$ var=$(echo "foo bar baz")  ## whitespace works
$  echo "$var"
foo bar baz

$ var=$(printf "foo\nbar * baz") ## so do globbing characters
$ echo "$var"
foo
bar * baz

$ var1="foo\nbar * baz"
$ var=$(printf "$var1")  ## printing a variable doesn't make any difference
$ echo "$var" 
foo
bar * baz

$ var=$(printf '%s\n' "$var1")
$ echo "$var"
foo\nbar * baz

$ var=$(printf -- '-e %s\n' "$var1") ## strings starting with - also work
$ echo "$var"
-e foo\nbar * baz

Por supuesto, las comillas son absolutamente necesarias si la sustitución del comando se usa directamente para cosas como command1 "$(command2)", pero ese no parece ser el caso cuando se asigna a una variable.

Entonces, ¿qué me estoy perdiendo? ¿Alguna vez se necesitan las cotizaciones? ¿Qué caso de esquina citará una sustitución de comando?al asignar su valor de retorno a una variableprotegerte de? ¿O siempre está bien no citar una sustitución de comando si es el lado derecho de una operación de asignación de variable?

Respuesta1

Tú hacesno necesitapara citar la expresión en el lado derecho de una tarea.

Lo que te irrita es que la otra respuestarecomiendapara citar de todos modos. Pero esto es sólo sobre el mantenimiento del código.

Considera lo siguientecorrectoejemplo:

DIRNAME=$(dirname "$FILE")
echo "debug: dirname is $DIRNAME"
ls "$DIRNAME"

Ahora, después de un tiempo de usar este script, puede pensar que el mensaje de depuración podría eliminarse. Entonces, usando un editor eliminarás la línea de eco. Luego notarás que ya ni siquiera necesitas la variable DIRNAME y simplemente mueves el lscomando para reemplazar el sitio izquierdo de la asignación. Ahora puedesolvide agregar las citas necesariasy terminas con estoguión roto:

ls $(dirname "$FILE")

La probabilidad de cometer tal error es aún mayor si el primer autor es un experto en shell pero el segundo editor es un novato.

Por supuesto, es discutible si estoRecomendación para evitar una función de shell portátil.es realmente útil. Personalmente lo hago principalmente como él recomienda. También hago esto para las tareas más "simples" como: var="${foo}"(incluidas también las llaves superfluas).

Respuesta2

Como referencia,El manual de Bash es claro al respecto.:

Una variable puede ser asignada mediante una declaración de la forma

name=[value]

Si no se proporciona el valor, a la variable se le asigna la cadena nula. Todos los valores se someten a expansión de tilde, expansión de parámetros y variables, sustitución de comandos, expansión aritmética y eliminación de comillas (detallado a continuación). [...]No se realiza la división de palabras., con la excepción de "$@" como se explica a continuación.No se realiza la expansión del nombre de archivo.

Sin división de palabras, sin expansión de nombres de archivos, por lo tanto, sin necesidad de comillas.


En cuanto a POSIX, sección2.9.1 Comandos simples:

2. Se ampliarán las palabras que no sean asignaciones variables o redirecciones. Si quedan campos después de su expansión Si quedan campos después de su expansión, el primer campo se considerará el nombre del comando y los campos restantes serán los argumentos del comando.
[...]
4. Cada asignación de variable se ampliará para la expansión de tilde, la expansión de parámetros, la sustitución de comandos, la expansión aritmética y la eliminación de comillas antes de asignar el valor.

No estoy seguro de si se supone que eso debe interpretarse en el sentido de que la división de campos ocurre solo para las expansiones realizadas en el paso 2. El paso 4 hacenomencionar la división de campos, aunque la sección sobreDivisión de campoTampoco menciona las asignaciones de variables como una excepción a la producción de múltiples campos.

información relacionada