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 eval
expande 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í string
se expande dentro de las comillas dobles, pero se $
debe escapar para que word
no 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 declare
en 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 hostname
evalú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.