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

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?

Simplemente use comillas simples alrededor de la cadena con las comillas dobles:

 ./test.sh this is '"some test"' 

Así que las comillas dobles dentro de las comillas simples también se interpretaron como cadenas.

Pero yo recomendaría poner toda la cadena entre comillas simples:

  ./test.sh 'this is "some test" ' 

Para entender qué está haciendo el shell o más bien interpretar argumentos en scripts, puedes escribir un pequeño script como este:

 #!/bin/bash echo $@ echo "$@" 

Luego verá y probará qué sucede cuando se llama a un script con diferentes cadenas de caracteres.

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 generalmente es exactamente lo que quiere si solo quiere volver a pasar los argumentos a otro progtwig.

¿Qué estás tratando de hacer y de qué manera no está funcionando?

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 syntax ${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 usted 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.

Si es seguro suponer que un argumento que contiene espacios en blanco debe haber sido (y debe ser) citado, entonces puede agregarlos 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 tabs literales y nuevas líneas usando CtrlV Tab y CtrlV CtrlJ 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.

Hay dos formas seguras de hacer esto:

1. Expansión de parámetros de shell : ${variable@Q}:

Al expandir una variable a través de ${variable@Q} :

La expansión es una cadena que es el valor del parámetro citado en un formato que puede reutilizarse como entrada.

Ejemplo:

 $ expand-q() { for i; do echo ${i@Q}; done; } # Same as for `i in "$@"`... $ expand-q word "two words" 'new > line' "single'quote" 'double"quote' word 'two words' $'new\nline' 'single'\''quote' 'double"quote' 

2. printf %q "$quote-me"

printf 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 $ 

Tenga en cuenta que la segunda forma es un poco más limpia si se muestra el texto entre comillas a un ser humano.

Relacionado: para bash, POSIX sh y zsh: cadena de comillas con comillas simples en lugar de barras invertidas

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. 

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ó! 🙂

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 ab 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 ab b $ ./send_fewer_args.sh "a' b" 'c "e ' c "e $ ./send_fewer_args.sh "a' b" 'c "e ' 'f " g' c "ef " g 

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" 

Necesitaba esto para reenviar todos los argumentos a otro intérprete. Lo que terminó bien para mí es:

 bash -c "$(printf ' %q' "$@")" 

Ejemplo (cuando se nombra como forward.sh):

 $ ./forward.sh echo "3 4" 3 4 $ ./forward.sh bash -c "bash -c 'echo 3'" 3 

(Por supuesto, el guión real que uso es más complejo, involucrando en mi caso nohup y redirecciones, etc., pero esta es la parte clave).

Sí, parece que no es posible conservar las citas, pero para el problema que estaba tratando no era necesario.

Tengo una función bash que buscará la carpeta recursivamente y grep para una cadena, el problema es pasar una cadena que tiene espacios, como “encontrar esta cadena”. Pasar esto al script bash tomará el argumento base $ n y lo pasará a grep, esto tiene grep creyendo que estos son argumentos diferentes. La forma en que lo resolví al usar el hecho de que cuando citan bash para llamar a la función agrupa los elementos de las comillas en un solo argumento. Solo necesitaba decorar ese argumento con comillas y pasarlo al comando grep.

Si sabe qué argumento está recibiendo en bash que necesita citas para su próximo paso, puede decorar con comillas.