¿Cómo mantener las citas en los argumentos Bash?


Tengo un script Bash donde quiero mantener las comillas en los argumentos pasados.

Ejemplo:

./test.sh this is "some test"

Entonces quiero usar esos argumentos, y reutilizarlos, incluyendo comillas y comillas alrededor de toda la lista de argumentos.

Intenté usar \"$@\", pero eso elimina las comillas dentro de la lista.

¿Cómo logro esto?

 54
Author: codeforester, 2009-11-03

11 answers

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 cadena.

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

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

Para entender lo que el shell está haciendo o más bien interpretando argumentos en scripts, puede escribir un pequeño script como este:

#!/bin/bash

echo $@
echo "$@"

Entonces verás y probarás, qué está pasando al llamar a un script con diferentes cadenas

 -10
Author: Randy Sugianto 'Yuku',
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-27 18:03:00

Usando "$@" reemplazará 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 desea si solo desea volver a pasar los argumentos a otro programa.

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

 69
Author: Chris Dodd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-31 22:55:24

La respuesta de Yuku solo funciona si eres el único usuario de tu script, mientras que la de Dennis Williamson es genial si estás principalmente interesado en imprimir las cadenas, y esperas que no tengan comillas entre comillas.

Aquí hay una versión que se puede usar si desea pasar todos los argumentos como un argumento de cadena de comillas grande 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 alrededor de ellos (inofensivo si no estaba allí antes, para este propósito), pero también escapamos de cualquier escape y luego escapamos de cualquier cita que ya estuviera en un argumento (la sintaxis ${var//from/to} hace sustitución de subcadenas globales).

Por supuesto, solo podría citar cosas que ya tenían espacios en blanco, pero no importará aquí. Una utilidad de un script como este es poder tener un determinado conjunto predefinido de variables de entorno(o, con su, ejecutar cosas como un determinado usuario, sin ese lío de comillas dobles de todo).


Actualización: Recientemente tenía razones para hacer esto de una manera POSIX con bifurcación mínima, que conducen a este script (el último printf allí muestra la línea de comandos utilizada para invocar el script, que debería ser capaz de copiar y pegar para invocarlo 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 conchas también interpretan cosas como $ y !! en ""-comillas.

 33
Author: unhammer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-07 12:32:09

Si es seguro hacer la suposición de que un argumento que contiene espacio en blanco debe haber sido (y debe ser) citado, entonces puede agregarlos de la siguiente manera:

#!/bin/bash
whitespace="[[:space:]]"
for i in "$@"
do
    if [[ $i =~ $whitespace ]]
    then
        i=\"$i\"
    fi
    echo "$i"
done

Aquí hay una ejecución de ejemplo:

$ ./argtest abc def "ghi jkl" $'mno\tpqr' $'stu\nvwx'
abc
def
"ghi jkl"
"mno    pqr"
"stu
vwx"

También puede insertar literal pestañas y saltos de línea se utiliza Ctrl-V Ficha y Ctrl-V Ctrl-J dentro de las dobles o comillas simples en lugar de utilizar escapa dentro de $'...'.

Una nota sobre insertando caracteres en Bash: Si está utilizando enlaces de teclas Vi (set -o vi) en Bash (Emacs es el valor predeterminado - set -o emacs), deberá estar en insert mode para insertar caracteres. En el modo Emacs, siempre estás en modo insert.

 30
Author: Dennis Williamson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-16 23:17:02

Hay dos maneras seguras de hacer esto:

1. Expansión del parámetro 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 se puede reutilizar 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 correspondiente argumento en un formato que puede ser reutilizado 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 2a manera es un poco más limpia si muestra el texto citado a un humano.

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

 11
Author: Tom Hale,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-31 08:37:14

Como dijo Tom Hale, una forma de hacer esto es usando printf %q para citar-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
 5
Author: Gary S. Weaver,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-09 22:24:39

Cambiado el ejemplo de unhammer para usar array.

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.                               
 4
Author: JohnMudd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:27

Mi problema era 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 1er 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 bash del servidor 1 (local) solo tenemos:

/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 discusiones con espacio.

Pero entonces en el servidor remoto no podemos usar la misma lógica porque el 1er servidor no recibirá las comillas de "$@". Utilicé 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 comillas escapadas solo cuando el elemento tiene espacios.

Así que el script remoto 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 uso "${CSMOPTS[@]}" para pasar la matriz de opciones al servidor remoto.

Gracias por ¡todos los que escribieron aquí! Realmente me ayudó! :)

 4
Author: Gus Neves,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-29 21:05:50

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 script real que uso es más complejo, involucrando en mi caso nohup y redirecciones, etc. pero esta es la parte clave.)

 4
Author: isarandi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-16 09:53:08

Las comillas son interpretadas por bash y no se almacenan en argumentos de línea de comandos o valores de variables.

Si quieres usar argumentos entre comillas, tienes que citarlos cada vez que los uses:

val="$3"
echo "Hello World" > "$val"
 3
Author: mouviciel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-03 17:03:06

Sí, parece que no es posible preservar las citas, pero para el tema con el que estaba tratando no era necesario.

Tengo una función bash que buscará en la carpeta recursivamente y grep para una cadena, el problema es pasar una cadena que tiene espacios, como "find this string". Pasar esto al script bash tomará el argumento base n n y lo pasará a grep, esto hace que grep crea que estos son argumentos diferentes. La forma en que resolví esto usando el hecho de que cuando comillas 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 comillas para su siguiente paso, puede decorar con comillas.

 0
Author: Tom Orsi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-20 16:55:51