Como dividir uma string com aspas (como argumentos de comando) no bash?

Como dividir uma string com aspas (como argumentos de comando) no bash?

Eu tenho uma string assim:

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

Eu quero ser capaz de dividir assim:

aString that may haveSpaces IN IT
bar
foo
bamboo  
bam boo

Como faço isso? (de preferência usando uma linha)

Responder1

Quando vi a resposta de David Postill, pensei “deve haver uma solução mais simples”. Depois de algumas experiências, encontrei os seguintes trabalhos: -

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

Isso funciona porque evalexpande a linha (removendo as aspas e expandindo string) antes de executar a linha resultante (que é a resposta in-line):

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

Uma alternativa que se expande para a mesma linha é:

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

Aqui stringé expandido entre aspas duplas, mas deve $ser escapado para que wordin não seja expandido antes da linha ser executada (na outra forma, o uso de aspas simples tem o mesmo efeito). Os resultados são: -

[~/]$ 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

Responder2

A solução mais simples é criar uma matriz dos argumentos citados, que você pode fazer um loop, se desejar, ou passar diretamente para um comando.

eval "array=($string)"

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

ps Por favor, comente se você encontrar uma maneira mais simples sem eval.

Editar:

Com base na resposta do @Hubbitus, temos uma versão totalmente higienizada e devidamente citada. Nota: isso é um exagero e, na verdade, deixará barras invertidas adicionais nas seções com aspas duplas ou simples que precedem a maior parte da pontuação, mas é invulnerável a ataques.

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

Deixo ao leitor interessado modificar como achar melhorhttp://ideone.com/FUTHhj

Responder3

Parece que xargs pode fazer isso muito bem:

$ 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

Responder4

Você pode fazer isso com declareem vez de eval, por exemplo:

Em vez 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'

Fazer:

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

Mas observe que não é muito mais seguro se a entrada vier do usuário!

Então, se você tentar dizer uma string como:

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

Você é hostnameavaliado (é claro que pode haver algo como rm -rf /)!

Uma tentativa muito simples de protegê-lo, basta substituir caracteres como backtrick ` e $:

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

Agora você obteve uma saída como:

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

Mais detalhes sobre métodos e prós e contras você pode encontrar nessa boa resposta:https://stackoverflow.com/questions/17529220/why-should-eval-be-avoided-in-bash-and-what-should-i-use-instead/17529221#17529221

Mas ainda restava vetor para ataque. Eu quero muito ter no método bash de citação de string como entre aspas duplas (") mas sem interpretar o conteúdo.

informação relacionada