A divisão de palavras não vê minhas citações

3

Imagine que eu tenho isso

$ ARGS='"a b" c'
$ for arg in "$ARGS"; do echo "$arg"; done
"a b" c
$ for arg in $ARGS; do echo "$arg"; done
"a
b"
c

O resultado que estou procurando é

$ <???>
a b
c

Como eu faria isso? Obrigado.

    
por Flame 28.07.2012 / 01:05

2 respostas

2

Isso daria a saída que você pede:

eval "for arg in $ARGS; do echo \"\$arg\";done"

Mas eval só deve ser usado como último recurso. A razão pela qual sua quetsion é difícil de responder é que o formato da sua variável ARGS é mal projetado.

Uma série de palavras separadas por espaços em branco, com espaços em branco dentro de palavras protegidas por um mecanismo de citações, é o formato básico de um comando shell. Já que você tem que aprender este formato para poder escrever um script de shell, torna-se tentador usá-lo para as estruturas de dados gerenciadas pelo seu shell script, então você pega a construção shell-like "a b" c e a coloca em um shell variável.

Isso geralmente é uma má ideia. Não há nenhuma razão para as strings no seu shell script parecerem com as linhas de comando do shell, e a única maneira de manipular uma string contendo palavras separadas por espaços em branco e talvez citadas é construir seu próprio parser fora das operações de string primitivas, ou perguntar o shell para fazer isso com eval.

Mas quando você usa eval, você não apenas pega o divisor de palavras e o removedor de citações, você também obtém todo o resto do analisador do shell: substituição de comando, substituição de parâmetro, redirecionamento de entrada / saída e outras coisas você provavelmente não quer.

Use uma matriz. Este script baseado em array funciona em zsh, ksh e bash:

ARGS=("a b" c)
for arg in "${ARGS[@]}"; do
    echo "$arg"
done

É muito mais limpo que eval. Se você está programando para um sistema antigo que não possui zsh, ksh ou bash, considere aprender o awk. O shell POSIX mínimo não é uma boa linguagem para scripts complicados o suficiente para precisar de matrizes.

Uma pequena elaboração sobre parte da resposta de ormaaj: POSIX sh tem uma única variável de matriz: $@ , que é a matriz de "parâmetros posicionais" (argumentos de linha de comando). Você pode acessar (ler, não gravar) seus elementos individuais como $1 , $2 , $3 , etc. Você pode substituir a matriz inteira pelo comando set . Para o seu script de exemplo, essa funcionalidade é quase insuficiente. Isso funciona em qualquer shell da família Bourne:

set -- "a b" c
for arg; do
    echo "$arg"
done

Eu adicionei uma demonstração do recurso de bônus: for arg sem um in é equivalente a for arg in "$@" , que faz um loop sobre a matriz $@ .

    
por 28.07.2012 / 07:14
0

Como você não se importa com as aspas, apenas remova-as com sed antes de percorrer a string:

for arg in 'echo $ARGS | sed -e 's/"//g''; do
  echo $arg
done

A menos que ... seu exemplo esteja errado e você queira ver algo assim:

a b
c

Provavelmente, existem muitas maneiras de fazer isso, mas eu usaria o awk (assumindo o GNU awk para que possamos usar separadores de campo maiores que um caractere):

echo $ARGS | \
awk -F'" ' '{ for (field = 1; field <= NF; field++) print $field; }' | \
sed -e 's/^"//' -e 's/"$//'

O último sed remove quaisquer cotações de abertura e uma cotação de fechamento final no final de $ ARGS. Claro, tudo isso vai desmoronar se você tiver citações aninhadas.

    
por 28.07.2012 / 01:23