script - ¿Cómo mantener las cotizaciones en los argumentos de Bash?
shift en bash (9)
Como dijo Tom Hale, una forma de hacerlo es con printf
usando %q
para quote-escape.
Por ejemplo:
send_all_args.sh
#!/bin/bash
if [ "$#" -lt 1 ]; then
quoted_args=""
else
quoted_args="$(printf " %q" "${@}")"
fi
bash -c "$( dirname "${BASH_SOURCE[0]}" )/receiver.sh${quoted_args}"
send_fewer_args.sh
#!/bin/bash
if [ "$#" -lt 2 ]; then
quoted_last_args=""
else
quoted_last_args="$(printf " %q" "${@:2}")"
fi
bash -c "$( dirname "${BASH_SOURCE[0]}" )/receiver.sh${quoted_last_args}"
receiver.sh
#!/bin/bash
for arg in "$@"; do
echo "$arg"
done
Ejemplo de uso:
$ ./send_all_args.sh
$ ./send_all_args.sh a b
a
b
$ ./send_all_args.sh "a'' b" ''c "e ''
a'' b
c "e
$ ./send_fewer_args.sh
$ ./send_fewer_args.sh a
$ ./send_fewer_args.sh a b
b
$ ./send_fewer_args.sh "a'' b" ''c "e ''
c "e
$ ./send_fewer_args.sh "a'' b" ''c "e '' ''f " g''
c "e
f " g
Tengo un script Bash en el que quiero mantener las comillas en los argumentos aprobados.
Ejemplo:
./test.sh this is "some test"
luego quiero usar esos argumentos y volver a usarlos, incluyendo citas y citas en toda la lista de argumentos.
Intenté usar /"$@/"
, pero eso elimina las comillas dentro de la lista.
¿Cómo logro esto?
La respuesta de Yuku solo funciona si usted es el único usuario de su secuencia de comandos, mientras que Dennis Williamson es excelente si está interesado principalmente en imprimir las cadenas, y espera que no tengan citas entre comillas.
Aquí hay una versión que puede usarse si desea pasar todos los argumentos como un argumento grande de cadena citada al parámetro -c
de bash
o su
:
#!/bin/bash
C=''''
for i in "$@"; do
i="${i/////////}"
C="$C /"${i///"////"}/""
done
bash -c "$C"
Entonces, todos los argumentos obtienen una cita a su alrededor (inofensivo si no estaba allí antes, para este propósito), pero también escapamos cualquier escape y luego escapamos de las comillas que ya estaban en un argumento (la sintaxis ${var//from/to}
hace la sustitución de subcadena global).
Por supuesto, solo puedes citar cosas que ya tienen espacios en blanco, pero no importará aquí. Una utilidad de un script como este es poder tener un cierto conjunto predefinido de variables de entorno (o, con su, ejecutar cosas como un usuario determinado, sin ese lío de citas dobles).
Actualización: Recientemente tuve una razón para hacer esto de forma POSIX con un mínimo de bifurcación, lo que condujo a este script (el último printf muestra la línea de comando utilizada para invocar el script, que debería poder copiar y pegar para invocar con argumentos equivalentes):
#!/bin/sh
C=''''
for i in "$@"; do
case "$i" in
*/'*)
i=`printf "%s" "$i" | sed "s/''/''/"''/"''/g"`
;;
*) : ;;
esac
C="$C ''$i''"
done
printf "$0%s/n" "$C"
Cambié a ''''
ya que las shells también interpretan cosas como $
y !!
en ""
-quotes.
Las citas se interpretan por bash y no se almacenan en argumentos de línea de comando o valores variables.
Si desea usar argumentos entrecomillados, debe citarlos cada vez que los use:
val="$3"
echo "Hello World" > "$val"
Mi problema fue similar y utilicé ideas mixtas publicadas aquí.
Tenemos un servidor con un script PHP que envía correos electrónicos. Y luego tenemos un segundo servidor que se conecta al primer servidor a través de SSH y lo ejecuta.
El nombre del script es el mismo en ambos servidores y ambos se ejecutan a través de un script bash.
En el script de bash del servidor 1 (local) tenemos justo:
/usr/bin/php /usr/local/myscript/myscript.php "$@"
Esto reside en /usr/local/bin/myscript
y es llamado por el servidor remoto. Funciona bien incluso para argumentos con espacios.
Pero luego en el servidor remoto no podemos usar la misma lógica porque el primer servidor no recibirá las cotizaciones de "$@"
. Usé las ideas de JohnMudd y Dennis Williamson para recrear la matriz de opciones y parámetros con las citas. Me gusta la idea de agregar citas escapadas solo cuando el elemento tiene espacios.
Entonces, la secuencia de comandos remota se ejecuta con:
CSMOPTS=()
whitespace="[[:space:]]"
for i in "$@"
do
if [[ $i =~ $whitespace ]]
then
CSMOPTS+=(/"$i/")
else
CSMOPTS+=($i)
fi
done
/usr/bin/ssh "$USER@$SERVER" "/usr/local/bin/myscript ${CSMOPTS[@]}"
Tenga en cuenta que utilizo "${CSMOPTS[@]}"
para pasar la matriz de opciones al servidor remoto.
¡Gracias por todos los que publicaron aquí! ¡Realmente me ayudó! :)
Se cambió el ejemplo de martillo para usar matriz.
printargs() { printf "''%s'' " "$@"; echo; }; # http://superuser.com/a/361133/126847
C=()
for i in "$@"; do
C+=("$i") # Need quotes here to append as a single array element.
done
printargs "${C[@]}" # Pass array to a program as a list of arguments.
Si es seguro suponer que un argumento que contiene espacios en blanco debe haber sido (y debe ser) citado, entonces puede agregarlo así:
#!/bin/bash
whitespace="[[:space:]]"
for i in "$@"
do
if [[ $i =~ $whitespace ]]
then
i=/"$i/"
fi
echo "$i"
done
Aquí hay una muestra de ejecución:
$ ./argtest abc def "ghi jkl" $''mno/tpqr'' $''stu/nvwx''
abc
def
"ghi jkl"
"mno pqr"
"stu
vwx"
También puede insertar pestañas literales y nuevas líneas usando Ctrl - V Tab y Ctrl - V Ctrl - J dentro de comillas dobles o simples en lugar de usar escapes dentro de $''...''
.
Una nota sobre la inserción de caracteres en Bash: si está utilizando enlaces de teclas Vi ( set -o vi
) en Bash (Emacs es el valor predeterminado - set -o emacs
), tendrá que estar en modo de inserción para insertar caracteres . En el modo Emacs, siempre estás en el modo de inserción.
el uso de "$@"
sustituirá los argumentos como una lista, sin volver a dividirlos en espacios en blanco (se dividieron una vez cuando se invocó el script de shell), que es generalmente exactamente lo que desea si solo quiere volver a pasar los argumentos a otro programa.
¿Qué estás tratando de hacer y de qué manera no está funcionando?
bash
admite cotizaciones internas. La entrada del manual para printf
dice:
%q
Hace que printf emita el argumento correspondiente en un formato que se puede reutilizar como entrada de shell.
Ejemplo:
$ cat test.sh
#!/bin/bash
printf "%q/n" "$@"
$
$ ./test.sh this is "some test" ''new
>line'' "single''quote" ''double"quote''
this
is
some/ test
$''new/nline''
single/'quote
double/"quote
$
Voilà.
./test.sh this is ''"some test"''