Expansão de strings entre aspas e não citadas

11
  1. for i in $(xrandr); do echo "$i" ; done
  2. for i in "$(xrandr)"; do echo "$i"; done
  3. for i in "$(xrandr)"; do echo $i; done

Eu entendo porque 1 difere de 2. Mas por que 3 dá uma saída diferente de 2? Por favor, explique a saída também. Como as cotações funcionam nas novas linhas?

    
por ManuelSchneid3r 10.06.2013 / 20:09

2 respostas

17

Uma variável sem aspas (como em $var ) ou substituição de comando (como em $(cmd) ou 'cmd' ) é o operador split + glob em shells similares a Bourne.

Ou seja, o conteúdo é dividido de acordo com o valor atual da variável $IFS special (que, por padrão, contém os caracteres space, tab e newline)

E cada palavra resultante dessa divisão está sujeita a geração de nome de arquivo (também conhecida como globbing ou expansão de nome de arquivo ), ou seja, eles são considerados padrões e são expandidos para a lista de arquivos que correspondem a esse padrão.

Portanto, em for i in $(xrandr) , o $(xrandr) , porque não está entre aspas, é dividido em sequências de espaço, tabulação e caracteres de nova linha. E cada palavra resultante dessa divisão é verificada quanto a nomes de arquivos correspondentes (ou deixada como está se eles não corresponderem a nenhum arquivo), e for faz um loop sobre todos eles.

Em for i in "$(xrandr)" , não estamos usando o operador split + glob como a substituição do comando é citada, portanto há uma passagem no loop no valor um : a saída de xrandr (sem os caracteres de nova linha à direita que comandam as tiras de substituição ).

No entanto, em echo $i , $i não é citada novamente, novamente o conteúdo de $i é dividido e sujeito a geração de nome de arquivo e esses são passados como argumentos separados para o echo comando (e echo exibe seus argumentos separados por espaços).

Então, lições aprendidas:

  • se você não quiser divisão de palavras ou geração de nome de arquivo , sempre cite expansões de variáveis e substituições de comandos
  • se você quiser dividir palavras ou geração de nome de arquivo , deixe-as sem aspas, mas defina $IFS de acordo e / ou ative ou desative a geração de nome de arquivo, se necessário ( set -f , set +f ).

Normalmente, no seu exemplo acima, se você quiser fazer um loop pela lista de palavras separadas em branco na saída de xrandr , será necessário:

  • deixe $IFS em seu valor padrão (ou desative-o) para dividir em espaços em branco
  • Use set -f para desativar a geração de nome de arquivo, a menos que você tenha certeza de que xrandr nunca produzirá nenhum caractere * ou ? ou [ (que são curingas usados nos padrões de geração de nome de arquivo)

E, em seguida, use somente o operador split + glob (somente deixe a substituição do comando ou a expansão variável sem aspas) na parte in do loop for :

set -f; unset -v IFS
for i in $(xrandr); do whatever with "$i"; done

Se você quiser fazer um loop pelas linhas (não vazias) da saída xrandr , será necessário definir $IFS como o caractere de nova linha:

IFS='
'
    
por 10.06.2013 / 21:41
4

Uma nova linha cotada é uma nova linha. Portanto, echo "$1" fornece um único argumento de linha de comando para echo, que então imprime as novas linhas diretamente.

Uma nova linha sem palavras é o espaço em branco. Portanto, echo $1 fornece muitos argumentos de linha de comando para echo, que os imprime um após o outro separados por espaços.

    
por 10.06.2013 / 20:31