¿Cómo dividir una cadena entre comillas (como argumentos de comando) en bash?

¿Cómo dividir una cadena entre comillas (como argumentos de comando) en bash?

Tengo una cadena como esta:

"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"

Quiero poder dividirlo así:

aString that may haveSpaces IN IT
bar
foo
bamboo  
bam boo

¿Cómo puedo hacer eso? (preferiblemente usando una sola línea)

Respuesta1

Cuando vi la respuesta de David Postill, pensé "debe haber una solución más sencilla". Después de experimentar un poco encontré los siguientes trabajos:-

string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"'
echo $string
eval 'for word in '$string'; do echo $word; done'

Esto funciona porque evalexpande la línea (eliminando las comillas y expandiendo string) antes de ejecutar la línea resultante (que es la respuesta en línea):

for word in "aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"; do echo $word; done

Una alternativa que se expande a la misma línea es:

eval "for word in $string; do echo \$word; done"

Aquí stringse expande dentro de las comillas dobles, pero se $debe escapar para que wordno se expanda antes de que se ejecute la línea (en la otra forma, el uso de comillas simples tiene el mismo efecto). Los resultados son: -

[~/]$ string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"'
[~/]$ echo $string
"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"
[~/]$ eval 'for word in '$string'; do echo $word; done'
aString that may haveSpaces IN IT
bar
foo
bamboo
bam boo
[~/]$ eval "for word in $string; do echo \$word; done"
aString that may haveSpaces IN IT
bar
foo
bamboo
bam boo

Respuesta2

La solución más simple es crear una matriz de los argumentos citados que luego puede recorrer si lo desea o pasar directamente a un comando.

eval "array=($string)"

for arg in "${array[@]}"; do echo "$arg"; done   

PD: Por favor comente si encuentra una forma más sencilla sin eval.

Editar:

Sobre la base de la respuesta de @Hubbitus, tenemos una versión completamente desinfectada y citada correctamente. Nota: esto es excesivo y, de hecho, dejará barras invertidas adicionales en las secciones entre comillas simples o dobles que preceden a la mayoría de los signos de puntuación, pero es invulnerable a los ataques.

declare -a "array=($( echo "$string" | sed 's/[][`~!@#$%^&*():;<>.,?/\|{}=+-]/\\&/g' ))"

Dejo al lector interesado modificarlo como mejor le parezca.http://ideone.com/FUTHhj

Respuesta3

Parece que xargs puede hacerlo bastante bien:

$ a='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"'
$ printf "%s" "$a" | xargs -n 1 printf "%s\n"
aString that may haveSpaces IN IT
bar
foo
bamboo
bam boo

Respuesta4

Puedes hacerlo con declareen lugar de eval, por ejemplo:

En lugar de:

string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"'
echo "Initial string: $string"
eval 'for word in '$string'; do echo $word; done'

Hacer:

declare -a "array=($string)"
for item in "${array[@]}"; do echo "[$item]"; done

Pero tenga en cuenta que no es mucho más seguro si la entrada proviene del usuario.

Entonces, si lo intentas con una cadena como:

string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo" `hostname`'

¡Te hostnameevalúan (por supuesto, puede haber algo así como rm -rf /)!

Un intento muy, muy simple de protegerlo, simplemente reemplace caracteres como backtrick ` y $:

string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo" `hostname`'
declare -a "array=( $(echo $string | tr '`$<>' '????') )"
for item in "${array[@]}"; do echo "[$item]"; done

Ahora tienes un resultado como:

[aString that may haveSpaces IN IT]
[bar]
[foo]
[bamboo]
[bam boo]
[?hostname?]

Puede encontrar más detalles sobre los métodos, los pros y los contras en esa buena respuesta:https://stackoverflow.com/questions/17529220/why-should-eval-be-avoided-in-bash-and-what-should-i-use-instead/17529221#17529221

Pero todavía quedaba un vector de ataque. Realmente quiero tener en bash el método de comillas de cadena como entre comillas dobles (") pero sin interpretar el contenido.

información relacionada