¿Se necesitan presupuestos para la asignación de variables locales?

36

¿Puedo omitir con seguridad las cotizaciones en el lado derecho de una asignación local?

function foo {
    local myvar=${bar}
    stuff()
}

Estoy interesado principalmente bash, pero cualquier información sobre casos de esquina en otros shells es bienvenida.

rahmu
fuente
Creo que no hay diferencia si está en una línea como la tienes en tu función. Las tareas no necesitan ser citadas. Ver mpi-sb.mpg.de/departments/rg1/teaching/unixffb-ss98/…
jirib el

Respuestas:

41

Se necesitan Cotizaciones en export foo="$var"o local foo="$var"(o readonly, typeset, declarey otras variables que se declara comandos ) en:

  • dash
  • el shde NetBSD (también basado en el shell Almquist).
  • El shde FreeBSD 9.2 o anterior (ver el cambio en 9.3 )
  • yash
  • zshcon versiones anteriores a 5.1 en ksho shemulación (o para export var="$(cmd)"dónde zshse dividiría la palabra de otra manera (no globular)).

De lo contrario, la expansión de la variable estaría sujeta a la división de palabras y / o generación de nombre de archivo como en cualquier argumento a cualquier otro comando.

Y no son necesarios en:

  • bash
  • ksh (todas las implementaciones)
  • el shde FreeBSD 9.3 o más reciente
  • busybox 'basado en cenizas sh(desde 2005)
  • zsh

En zsh, split + glob nunca se realiza tras la expansión de parámetros, a menos que sea en sho kshemulación, pero split (no glob) se realiza tras la sustitución del comando. Desde la versión 5.1, export/ localy otros comandos de declaración se han convertido en comandos duales de palabras clave / incorporados como en los otros shells anteriores, lo que significa que no es necesario citar, incluso en sh/ kshemulación e incluso para la sustitución de comandos.

Hay casos especiales en los que es necesario citar incluso en esos depósitos, como:

a="b=some value"
export "$a"

O más en general, en todo caso queda de la =(incluyendo el =) es citado o el resultado de una cierta expansión (como export 'foo'="$var", export foo\="$var"o export foo$((n+=1))="$var"(que $((...))también debe citarse en realidad) ...). O en otras palabras, cuando el argumento de que exportno sería una asignación de variables válidas si son escritos sin el export.

Si el export/ localnombre del comando en sí es citado (aunque sea en parte, como "export" a="$b", 'ex'port a="$b", \export a="$b", o incluso ""export a="$b"), las comillas $bson necesarias excepto en AT & T kshy mksh.

Si export/ localo alguna parte es el resultado de una expansión (como en cmd=export; "$cmd" a="$b"o incluso export$(:) a="$b") o en cosas como dryrun=; $dryrun export a="$b"), entonces las comillas son necesarias en cada shell.

En el caso de > /dev/null export a="$b", son necesarias las comillas en pdkshy algunos de sus derivados.

Para command export a="$b", las comillas son necesarias en cada shell pero mkshy ksh93(con las mismas advertencias sobre commandy exportno ser el resultado de alguna expansión).

Ellos no son necesarios en cualquier shell cuando se escribe:

foo=$var export foo

(que la sintaxis ser también compatible con el shell Bourne pero en versiones recientes de zsh, trabajar sólo cuando en sh/ kshemulación).

(Tenga en cuenta que var=value local varno debe ser utilizado como el comportamiento varía a través de conchas).

También tenga en cuenta que el uso exportde una asignación también significa que el estado de salida cmdde export var="$(cmd)"se pierde. Hacerlo como export var; var=$(cmd)no tiene ese problema.

También tenga cuidado con este caso especial con bash:

$ bash -c 'IFS=; export a="$*"; echo "$a"' bash a b
ab
$ bash -c 'IFS=; export a=$*; echo "$a"' bash a b
a b

Mi consejo sería citar siempre.

Stéphane Chazelas
fuente
3
Tenga en cuenta que las zshcomillas son necesarias local foo="$(cmd)"porque la división de palabras (pero no la generación de nombre de archivo) se realiza para sustituciones de comandos sin comillas (pero no para expansiones de parámetros sin comillas), a menos que KSH_TYPESETesté habilitado, en cuyo caso no se necesitan comillas . ¿Tener sentido? ¿No? Luego, siempre cita todo a menos que sepas exactamente lo que estás haciendo.
Matt
2
@ Matt, me encanta tu conclusión. : D Es curioso, la mayor parte de lo que aprendí sobre el script de shell provino de este intercambio de pila, por lo que no me di cuenta de que siempre citar sus variables no es de conocimiento común entre los escritores de guiones. Me doy cuenta de que tengo que arreglar mucho los guiones de producción existentes escritos por personas que no citaron, y que no sabían exactamente lo que estaban haciendo ...
Comodín el
3

En general, cito cualquier uso de variables donde pueda haber caracteres como espacios en blanco. De lo contrario, tendrás problemas como este:

#!/bin/bash

bar="hi bye"

function foo {
  local myvar=${bar}
  printf "%s\n" $myvar
  printf "%s\n" "$myvar"
}

foo

El uso de la variable en una asignación no parece necesitar las comillas, pero cuando vaya a usarla como en la printfnecesitará citada allí:

  printf "%s\n" "$myvar"

NOTA: Recuerde que la variable $IFSes lo que gobierna cuáles son los caracteres separadores.

IFS    The  Internal  Field  Separator that is used for word splitting after 
       expansion and to split lines into words with the read builtin command. 
       The default value is ``<space><tab><newline>''.

Ejemplo

Con la depuración habilitada en Bash podemos ver lo que sucede detrás de escena.

$ bash -x cmd.bash 
+ bar='hi bye'
+ foo
+ local 'myvar=hi bye'
+ printf '%s\n' hi bye
hi
bye
+ printf '%s\n' 'hi bye'
hi bye

En lo anterior, podemos ver que la variable $barse transmitió bien, $myvarpero luego, cuando la usamos $myvar, teníamos que ser conscientes del contenido de $myvarcuándo la usamos.

slm
fuente
2
la división de palabras no es el único problema con las variables sin comillas, también debe tener en cuenta la generación de nombre de archivo (también conocido como globbing) (aunque eso (ambos) no se aplica en asignaciones de variables y for bashand kshin local/ typeset... builtins especiales).
Stéphane Chazelas