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

6

Eu tenho uma string como esta:

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

Eu quero dividir assim:

aString that may haveSpaces IN IT
bar
foo
bamboo  
bam boo

Como faço isso? (preferencialmente usando um one-liner)

    
por foxneSs 17.04.2016 / 11:49

5 respostas

2

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 eval expande a linha (removendo as aspas e expandindo string ) antes de executar a linha resultante (que é a resposta imediata):

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 o $ deve ter escape para que word 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
    
por 17.04.2016 / 18:21
3

Como faço isso?

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

O que eu faço se minha string estiver em uma variável bash ?

A abordagem simples de usar o tokenizador de string bash não funcionará, pois ele se divide em todos os espaços e não apenas nos de fora:

DavidPostill@Hal /f/test
$ cat ./test.sh
#! /bin/bash
string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"'
for word in $string; do echo "$word"; done

DavidPostill@Hal /f/test
$ ./test.sh
"aString
that
may
haveSpaces
IN
IT"
bar
foo
"bamboo"
"bam
boo"

Para contornar este, o script de shell a seguir (splitstring.sh) mostra uma abordagem:

#! /bin/bash 
string=$(cat <<'EOF'
"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo" 
EOF
)
echo Source String: "$string"
results=()
result=''
inside=''
for (( i=0 ; i<${#string} ; i++ )) ; do
    char=${string:i:1}
    if [[ $inside ]] ; then
        if [[ $char == \ ]] ; then
            if [[ $inside=='"' && ${string:i+1:1} == '"' ]] ; then
                let i++
                char=$inside
            fi
        elif [[ $char == $inside ]] ; then
            inside=''
        fi
    else
        if [[ $char == ["'"'"'] ]] ; then
            inside=$char
        elif [[ $char == ' ' ]] ; then
            char=''
            results+=("$result")
            result=''
        fi
    fi
    result+=$char
done
if [[ $inside ]] ; then
    echo Error parsing "$result"
    exit 1
fi

echo "Output strings:"
for r in "${results[@]}" ; do
    echo "$r" | sed "s/\"//g"
done

Saída:

$ ./splitstring.sh
Source String: "aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"
Output strings:
aString that may haveSpaces IN IT
bar
foo
bamboo
bam boo

Fonte: StackOverflow answer Divida uma string apenas por espaços que estão fora das aspas por choroba . O script foi ajustado para atender aos requisitos da pergunta.

    
por 17.04.2016 / 13:24
3

A solução mais simples é usar a criação de um array dos args citados que você poderia então fazer um loop se quiser ou passar diretamente para um comando.

eval "array=($string)"

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

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

Editar:

Com base na resposta do @Hubbitus, temos uma versão totalmente saneada e devidamente citada. Nota: isso é um exagero e deixará barras invertidas adicionais em seções duplas ou simples citadas antes da maioria da pontuação, mas é invulnerável ao ataque.

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

Deixo para o leitor interessado modificar como achar melhor link

    
por 06.11.2016 / 12:13
2

Você pode fazer isso com declare em 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'

Faça:

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

Mas, por favor, note que não é muito mais seguro se a entrada vem do usuário!

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

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

Você recebe hostname avaliado (claro que pode haver algo como rm -rf / )!

Tentativa muito simples de guardá-lo, basta substituir chars 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ê tem 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 que você pode encontrar nessa boa resposta: link

Mas ainda há vetor de folhas 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 .

    
por 09.03.2017 / 18:12
0

use o awk

echo '"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"' | awk 'BEGIN {FPAT = "([^ ]+)|(\"[^\"]+\")"}{for(i=1;i<=NF;i++){gsub("\"","",$i);print $i} }'
aString that may haveSpaces IN IT
bar
foo
bamboo
bam boo

Ou converta o espaço para "% 20" ou "_", para que ele possa ser processado pelo próximo comando throw pip :

echo '"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"' | awk 'BEGIN {FPAT = "([^ ]+)|(\"[^\"]+\")"}{for(i=1;i<=NF;i++){gsub("\"","",$i);gsub(" ","_",$i)} print }'
aString_that_may_haveSpaces_IN_IT bar foo bamboo bam_boo

referência: O Awk considera aspas duplas como um símbolo e ignora o espaço entre

    
por 13.08.2018 / 06:31