¿Cómo sustituir cadenas citadas de múltiples palabras como argumentos?

Estoy tratando de sustituir una variable de cadena, que contiene varias palabras entrecomilladas, como un parámetro de un comando.

Por lo tanto, dado el siguiente script de ejemplo (tenga en cuenta el -x en el shebang, que hace que el resultado se registre en stderr),

#!/bin/bash -x myArg="\"hello\" \"world\"" echo "string is:" $myArg exit 

Lo que nos da,

 + myArg='"hello" "world"' + echo 'string is:' '"hello"' '"world"' string is: "hello" "world" + exit 

La línea dos muestra lo que en realidad se pasa al comando; bash ha agregado comillas simples a cada palabra en la cadena. Si en su lugar, por lo tanto, cito “$ myArg”, ocurre lo mismo, pero para toda la cadena en lugar de cada palabra.

Ahora, imagina que en lugar de echo , estamos pasando la cadena a un progtwig donde algunos de los argumentos necesitan ser patrones citados, como "*" (que no debe ser expandido por el shell).

Para aclarar, no quiero que se agreguen las comillas simples durante la expansión. ¿Cómo podría lograr esto?

No use comillas, use una matriz (vea BashFAQ # 050 ):

 $ myArgs=("hello" "world" "multiword arg with * ?") + myArgs=("hello" "world" "multiword arg with * ?") $ echo "${myArgs[@]}" + echo hello world 'multiword arg with * ?' hello world multiword arg with * ? 

Si realmente tiene que ser en forma de cadenas entrecomilladas dentro de una cadena, vas a tener que usar algo como eval "echo $myArg" (que puede causar algunos errores realmente desagradables, si no tienes cuidado) o analízalo tú mismo (lo cual será difícil).

Si desea pasar un valor de variable como parámetro (99% de los casos en SO), simplemente use las comillas correctas:

 arg="foo bar" command "$arg" 

Si quiere pasar varios argumentos, use matrices:

 args=("foo bar" "baz ban" bay) command "${args[@]}" 

No creo que esté haciendo lo que crees que está haciendo.

 [~]$ myArg="\"hello\" \"world\"" [~]$ echo "string is:" $myArg string is: "hello" "world" 

No veo presupuestos extra de ningún tipo: echo obtiene tres cadenas de argumentos.

 [~]$ cargs(){ echo $#; } [~]$ cargs "string is:" $myArg 3 

Bash expandirá la variable primero, entonces

 cargs "string is:" $myArg 

se convierte (aunque sin las barras invertidas literales, esta es la razón por la cual el escape de cuerdas es un PITA)

 cargs "string is:" "\"hello\"" "\"world\"" 

Y la matriz args es:

 0x00:string is:0 0x0B:"hello"0 0x13:"world"0 0x1B:0 

Ahora, si agrega la expansión de ruta * , o glob en una de esas, Bash la expandirá en este punto, a menos que la escape, o use comillas simples en su comando literal.

 [~]$ cargs "string is:" $myArg * 19 [~]$ cargs "string is:" $myArg "\*" 4 [~]$ cargs "string is:" $myArg '*' 4 

Hay una forma portátil de dividir expandir una variable pero mantener espacios. Bash arrays no son necesarios. Dash (Ubuntu / bin / sh) también funcionaría.

Usa algún personaje para separar argumentos que definitivamente no se usan dentro de los argumentos. El siguiente ejemplo usa punto y coma, pero podría ser una nueva línea u otro carácter. Cambie la variable IFS a una nueva línea temporalmente cuando se expande la lista de argumentos. Restaure IFS al valor original tan pronto como sea posible, incluso si eso significa hacerlo en el ciclo. Si no se garantiza que el ciclo se ejecute al menos una vez, hágalo después del ciclo también.

 #! /bin/sh arg_list='hello world;have a nice day' save_IFS="$IFS" IFS=';' for i in $arg_list; do IFS="$save_IFS" echo "$i" done IFS="$save_IFS" 

Tenga en cuenta que cada argumento expandido se imprime individualmente.

 $ ./test.sh hello world have a nice day