Bash -c con parámetros posicionales

15

Por lo general, $0en una secuencia de comandos se establece el nombre de la secuencia de comandos, o lo que se invocó como (incluida la ruta). Sin embargo, si lo uso bashcon la -copción, $0se establece en el primero de los argumentos pasados ​​después de la cadena de comando:

bash -c 'echo $0 ' foo bar 
# foo 

En efecto, parece que los parámetros posicionales se han cambiado, pero incluidos $0. Sin embargo, shiften la cadena de comando no afecta $0(como es normal):

bash -c 'echo $0; shift; echo $0 ' foo bar
# foo
# foo

¿Por qué este comportamiento aparentemente extraño para las cadenas de comandos? Tenga en cuenta que estoy buscando la razón, la justificación, detrás de la implementación de un comportamiento tan extraño.


Se podría especular que una cadena de comandos de este tipo no necesitaría el $0parámetro como se define generalmente, por lo que para economía también se usa para argumentos normales. Sin embargo, en ese caso el comportamiento de shiftes extraño. Otra posibilidad es que $0se usa para definir el comportamiento de los programas (a la bashllamada as sho vimllamada as vi), pero eso no puede ser, ya que $0aquí solo se ve en la cadena de comandos y no por los programas llamados dentro de ella. No puedo pensar en ningún otro uso $0, así que no puedo explicarlo.

muru
fuente
Como nota al margen, he visto el uso de -para el $0argumento como un modismo, como en sh -c 'foo $1 $2' - a b. De esta manera se ve bastante normal (una vez que descubriste lo que eso -significa, eso es)
Volker Siegel
Sin echo 'echo the other side of this pipe globs "$@"' | sh -s -- *embargo, también puede , desafortunadamente, $0generalmente no ser un parámetro configurable con la -sopción de tream ... Sin embargo, se puede usar de muchas de las mismas maneras xargs. Y otros además.
mikeserv
@VolkerSiegel Más normal hubiera sido --, entonces eso podría haber tenido la interpretación habitual de 'desde aquí comienza los argumentos', visto en algunos otros programas. Por otra parte, eso podría confundir a aquellos que no están familiarizados con -cpensar que --realmente tienen esa interpretación.
muru

Respuestas:

10

Eso le da la oportunidad de configurar / elegir $0cuando usa un script en línea. De $0lo contrario, simplemente sería bash.

Entonces puedes hacer, por ejemplo:

$ bash -c 'wc -c < "${1?}"' getlength foo
4
$ bash -c 'wc -c < "${1?}"' getlength bar
getlength: bar: No such file or directory
$ bash -c 'wc -c < "${1?}"' getlength
getlength: 1: parameter null or not set

No todos los proyectiles solían hacer eso. El caparazón Bourne lo hizo. El shell Korn (y Almquist) eligió tener el primer parámetro en su $1lugar. POSIX con el tiempo se fue por la forma de Bourne, de modo kshy ashderivados volvía al nivel más tarde (más sobre esto en http://www.in-ulm.de/~mascheck/various/find/#shell ). Eso significó que durante mucho tiempo sh(que dependiendo del sistema se basó en el shell Bourne, Almquist o Korn), no sabías si el primer argumento entró $0o $1, por lo que para la portabilidad, tenías que hacer cosas como:

sh -c 'echo foo in "$1"' foo foo

O:

sh -c 'shift "$2"; echo txt files are "$@"' tentative-arg0 3 2 *.txt

Afortunadamente, POSIX ha especificado el nuevo comportamiento donde entra el primer argumento $0, por lo que ahora podemos hacer de forma portátil:

sh -c 'echo txt files are "$@"' meaningful-arg0-for-error *.txt
Stéphane Chazelas
fuente
Acabo de funcionar: bash -c 'echo txt files are "$@"' meaningful-arg0-for-error *.txten Ubuntu 14.04, fiesta version 4.3.11(1)-release, y tengo: txt files are *.txt. Oo
muru
2
@muru, que es correcto (y probablemente no tenga txtarchivos en el directorio actual). Ver tambiénbash -c 'echo "${1?}"' foo
Stéphane Chazelas
Ah, sí. Ese es un ejemplo prácticamente útil.
muru
1
sh -c 'shift "$2"; echo txt files are "$@"' tentative-arg0 3 2 *.txtes fantásticamente inventivo!
iruvar
O usa una solución completamente sólida (sugerida por Stéphane Chazelas en comp.unix.shell) SHELL -c 'shift $1; command' 2 1 arg1 arg2 ... jaja ...
mikeserv
3

POSIX define este comportamiento :

sh -c nombre_comando [argumento ...]

Lea los comandos del operando command_string. Establezca el valor del parámetro especial 0 (consulte Parámetros especiales ) a partir del valor del operando nombre_comando y los parámetros posicionales ($ 1, $ 2, etc.) en secuencia de los operandos de argumento restantes.

En cuanto a por qué querrías ese comportamiento: esto suaviza la brecha entre un script y una -ccadena. Puede convertir directamente entre los dos sin ningún cambio de comportamiento. Otras áreas confían en que estas sean idénticas.

También está en línea con la forma en que funcionan los argumentos del programa en general: en última instancia, esto se reduce a llamar a una de las execfunciones, en la que también está el primer argumento proporcionado $0, e igualmente comúnmente ese argumento es el mismo que el ejecutable que está ejecutando. A veces, sin embargo, desea un valor especial allí, y simplemente no habría otra forma de obtenerlo. Dado que el argumento existe, tiene que asignarse a algo, y el usuario debe poder establecer qué es eso.

Esta coherencia (y probable accidente histórico) conduce a la situación que encuentra.

Michael Homer
fuente
¿Puedes dar un ejemplo (con suerte del mundo real) del segundo párrafo?
muru
Cuidado con la analogía con argv[0]. $0solo se establece en el argv[0]pasado a execve()cuando es como sho bash. Para los scripts, $0se establece en la ruta dada como argumento 1, 2 ... al intérprete (y para los scripts ejecutados directamente, eso proviene del primer argumento de ruta a execve ()).
Stéphane Chazelas