Una respuesta comoesteHace un buen trabajo explicando cómo controlar el pase.todovariables hasta un comando.
Quería explorar cómo hacer esto por argumento. Observe (esto fue probado en zsh
):
$ program() { echo 1: $1 2: $2 3: $3; }
$ run() { program "$@"; }
$ run2() { echo `run $1`; }
$ run2 'a b' c
1: a b 2: 3:
Quiero una forma de pasar un primer argumento que es una cadena que tiene espacios y tener ese $@
estilo empalmado en otro comando. por ejemplo, run2 "a b" c
debería producir1: a 2: b 3:
En este punto, he resuelto mi problema inmediato porque, aunque mi código de prueba se rompe cuando lo probé en zsh, una vez implementado en un script bash real, funciona.
Esto indica que tal vez esto dependa de algunas complejidades en el manejo de cadenas y argumentos que no son "seguras". Por lo tanto, esto es más bien una solicitud de comentarios sobre una forma más sólida de lograr este comportamiento de manera confiable.
Respuesta1
La diferencia entre bash y zsh que importa aquí está en la forma de run2
llamar run
, específicamente el efecto de dejarlo $1
sin comillas.
- En zsh,
run $1
aplica el operador "remove-if-empty" a$1
, es decir, llamarun
con el primer argumento que se pasó arun2
, excepto que si el primer argumentorun2
estaba vacío (o sirun2
se llamó sin argumento), entoncesrun
se llama sin argumento. - En otros shells estilo Bourne, como bash,
run $1
se aplica el operador “split+glob” a$1
, es decir, divide el primer argumentorun2
en fragmentos separados por espacios¹, interpreta cada pieza como un patrón comodín² y reemplaza cada patrón comodín que coincida con uno o más archivo por la lista de coincidencias.
Por lo tanto, run2 'a b' c
llama run
con el argumento a b
en zsh (el argumento se pasa sin cambios), pero llama run
con los dos argumentos a
y b
en bash (dividido en partes delimitadas por espacios en blanco).
Quiero una forma de pasar un primer argumento que es una cadena que tiene espacios y tener ese
$@
estilo empalmado en otro comando. por ejemplo,run2 "a b" c
debería producir1: a 2: b 3:
Tu descripción y tu ejemplo dicen cosas diferentes. Si desea pasar el primer argumento al otro comando, utilice run "$1"
para asegurarse de que el argumento no esté dividido. Pasar argumentos sin cambios es el objetivo de "$@"
.
Parece que lo que realmente quieres hacer es dividir el primer argumento en run2
fragmentos delimitados por espacios en blanco. En bash, puede hacer esto desactivando la expansión con comodines y luego (suponiendo que IFS
no haya cambiado con respecto al valor predeterminado) usando una expansión sin comillas.
run2 () ( set -f; run $1; )
( echo "$(somecommand)"
es esencialmente equivalente a ejecutar somecommand
en un subshell, y parece que esto es lo que quiso decir en lugar de echo $(somecommand)
aplicar split+glob en la salida del comando, por lo que eliminé la sustitución de comando de eco redundante).
En zsh, puede usar el =
carácter en una sustitución de parámetros para realizar la división del mundo (y no globalización) en el valor.
run2 () { run $=1; }
La sintaxis de zsh no es compatible con la de sh simple. Si desea obtener un script sh de zsh, puede utilizar la emulate
compilación:
emulate sh -c '. myscript.sh'
Úselo emulate ksh
para emular (algunas características de) ksh. Esto no emula todas las funciones de bash, pero le permite usar matrices.
¹ De manera más general, basado en el valor de IFS
.
² A menos que esto se haya desactivado con set -f
.
Respuesta2
Bienvenido al mundo de las citas y las sorpresas.
El problema principal es que zsh
no se divide en caracteres IFS de forma predeterminada.
En eso: se diferencia de todos los demás caparazones.
Para probar cómo las comillas cambian la forma en que funcionan los shells, necesitamos un código que pruebe las versiones entre comillas y sin comillas de su código.
Su código (agregando un par de variables):
program() { printf '%02d-1: %6s 2: %6s 3: %6s' "$i" "$1" "$2" "$3"; }
runa() { program "$@" ; }
run1() { echo "`runa $1 $2 $3 `"; }
run1 'a b' c
Permítanme ampliar los detalles.
Supongamos que crea un sh
enlace local que apunta al lugar donde zsh
vive:
ln -s "/usr/bin/zsh" ./sh
Y, además, supongamos que copia el siguiente script en un so
archivo.
Un script que repite cada función con variables entre comillas y sin comillas:
program() { printf '%02d-1: %6s 2: %6s 3: %6s' "$i" "$1" "$2" "$3"; }
runa() { program "$@"; }
runb() { program $@ ; }
run1() { echo "`runa "$1" "$2" "$3"`"; }
run2() { echo "`runb "$1" "$2" "$3"`"; }
run3() { echo "`runa $1 $2 $3`"; }
run4() { echo "`runb $1 $2 $3`"; }
for i in `seq 4`; do
run"$i" 'a b' c
done
Luego, al ejecutar, obtenemos esto impreso:
# Any shell (except zsh) results.
01-1: a b 2: c 3:
02-1: a 2: b 3: c
03-1: a 2: b 3: c
04-1: a 2: b 3: c
Sólo se mantiene unida la primera ejecución (ejecución1), donde se cita todo 'a b'
.
Sin embargo, zsh actúa como si todo hubiera sido citado todo el tiempo:
# ZSH results.
01-1: a b 2: c 3:
02-1: a b 2: c 3:
03-1: a b 2: c 3:
04-1: a b 2: c 3:
zsh en emulación.
Se supone que zsh
emulará shells más antiguos si se llama como sh
o ksh
.
Pero en la práctica esto no siempre es cierto:
$ ./sh ./so # emulated sh
01-1: a b 2: c 3:
02-1: a b 2: c 3:
03-1: a 2: b 3: c
04-1: a 2: b 3: c
La segunda línea es diferente a la segunda línea de cualquier otro shell.
Es una buena idealee las respuestas a esta pregunta