script - variables en bash




¿Cómo mantener las cotizaciones en los argumentos de Bash? (6)

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 \"[email protected]\" , pero eso elimina las comillas dentro de la lista.

¿Cómo logro esto?


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 "[email protected]"; 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

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 "[email protected]"; 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 "[email protected]"; 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.


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 "[email protected]"

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 "[email protected]" . 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 "[email protected]"
do
    if [[ $i =~ $whitespace ]]
    then
        CSMOPTS+=(\"$i\")
    else
        CSMOPTS+=($i)
    fi
done

/usr/bin/ssh "[email protected]$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' " "[email protected]"; echo; };  # http://superuser.com/a/361133/126847

C=()
for i in "[email protected]"; 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.                               

el uso de "[email protected]" 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" "[email protected]"
$ 
$ ./test.sh this is "some test" 'new                                                                                                              
>line' "single'quote" 'double"quote'
this
is
some\ test
$'new\nline'
single\'quote
double\"quote
$

Voilà.





quotes