Expansão de string 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

Entendo por que 1 difere de 2. Mas por que 3 fornece uma saída diferente de 2? Por favor, explique a saída também. Como as cotações funcionam em novas linhas?

ManuelSchneid3r
fonte

Respostas:

19

Uma variável não citada (como em $var) ou substituição de comando (como em $(cmd)ou `cmd`) é o operador split + glob em shells semelhantes a Bourne.

Ou seja, seu conteúdo é dividido de acordo com o valor atual da $IFSvariável especial (que por padrão contém os caracteres de espaço, tabulação e nova linha)

E então, cada palavra resultante dessa divisão está sujeita à geração do nome do arquivo (também conhecido como globbing ou expansão do nome do arquivo ), ou seja, elas são consideradas como padrões e são expandidas para a lista de arquivos que correspondem a esse padrão.

Assim for i in $(xrandr), em , $(xrandr)porque não está entre aspas, é dividido em seqüências de caracteres de espaço, tabulação e nova linha. E cada palavra resultante dessa divisão é verificada quanto a nomes de arquivos correspondentes (ou deixados como se não correspondam a nenhum arquivo) e forpassa por cima de todos eles.

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

No entanto echo $i, em , $ié sem aspas novamente, portanto, novamente o conteúdo de $ié dividido e sujeito à geração do nome do arquivo e esses são passados ​​como argumentos separados para o echocomando (e echogera seus argumentos separados por espaços).

Então, lição aprendida:

  • se você não deseja dividir palavras ou gerar nome de arquivo , sempre cite expansões variáveis ​​e substituições de comandos
  • se você deseja dividir palavras ou gerar nome de arquivo , deixe-os sem aspas, mas defina de $IFSacordo 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 sobre a lista de palavras em branco e separadas na saída de xrandr, precisará:

  • deixe $IFSem seu valor padrão (ou desative-o) para dividir em espaços em branco
  • Use set -fpara desativar a geração de nome de arquivo, a menos que você tenha certeza de que xrandrnunca gera nenhum *ou ?ou [caracteres (que são caracteres curinga usados ​​nos padrões de geração de nome de arquivo)

E, em seguida, use apenas o operador split + glob (deixe apenas a substituição de comando ou a expansão variável sem aspas) na inparte do forloop:

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

Se você deseja fazer um loop sobre as linhas (não vazias) da xrandrsaída, precisará definir $IFSo caractere de nova linha:

IFS='
'
Stéphane Chazelas
fonte
4

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

Uma nova linha não citada é um espaço em branco. Isso echo $1fornece muitos argumentos de linha de comando para eco, que os imprimem um após o outro separados por espaços.

rici
fonte
Uma nova linha não citada é que espaço em branco é um atalho demais para ser realmente útil como uma explicação do comportamento aqui da IMO.
Stéphane Chazelas