O que é a divisão de palavras? Por que isso é importante na programação de shell?

16

Estou ficando confuso sobre o papel que a divisão de palavras desempenha zsh. Eu não fui exposto a esse conceito ao programar em C, Python ou MATLAB, e isso despertou meu interesse em saber por que a divisão de palavras parece ser algo específico da programação em shell.

Já li sobre a divisão de palavras neste e em outros sites, mas não encontrei uma explicação clara do conceito. A Wikipedia tem uma definição de divisão de palavras, mas parece não ter referências sobre como se aplica aos shells do Unix.

Aqui está um exemplo da minha confusão em zsh:

Nas Perguntas frequentes sobre o Z Shell , li o seguinte:

3.1: Por $varque var="foo bar"não faz o que eu espero?

Na maioria dos derivados de Bourne-shell, variáveis ​​de várias palavras, como var="foo bar" são divididas em palavras, são passadas para um comando ou usadas em um for foo in $varloop. Por padrão, o zsh não tem esse comportamento: a variável permanece intacta. (Isso não é um bug! Veja abaixo.) A opção SH_WORD_SPLITexiste para fornecer compatibilidade.

No entanto, no Manual do Z Shell , li o seguinte:

SH_WORD_SPLIT (-y) <K> <S>

Faz com que a divisão de campos seja realizada em expansões de parâmetros não citadas. Observe que esta opção não tem nada a ver com a divisão de palavras. (Consulte Expansão de parâmetros.)

Por que diz que nadaSH_WORD_SPLIT tem a ver com a divisão de palavras? A divisão de palavras não é exatamente do que se trata?

Amelio Vazquez-Reina
fonte

Respostas:

21

Os shells anteriores tinham apenas um único tipo de dados: strings. Mas é comum manipular listas de strings, normalmente ao passar vários nomes de arquivos como argumentos para um programa. Outro caso de uso comum para divisão é quando um comando gera uma lista de resultados: a saída do comando é uma sequência, mas os dados desejados são uma lista de sequências. Para armazenar uma lista de nomes de arquivos em uma variável, você deve colocar espaços entre eles. Em seguida, um script de shell como este

files="foo bar qux"
myprogram $files

chamado myprogramcom três argumentos, enquanto o shell divide a string $filesem palavras. Na época, os espaços nos nomes dos arquivos eram proibidos ou amplamente considerados Não Concluídos.

O shell Korn introduziu matrizes: você pode armazenar uma lista de strings em uma variável. O shell Korn permaneceu compatível com o Bourne, então estabelecido, então as expansões variáveis ​​simples continuavam sendo divididas em palavras, e o uso de matrizes exigia alguma sobrecarga sintática. Você escreveria o trecho acima

files=(foo bar qux)
myprogram "${files[@]}"

O Zsh possuía matrizes desde o início, e seu autor optou por um design de linguagem mais saudável às custas da compatibilidade com versões anteriores. No zsh (sob as regras de expansão padrão) $var, não é possível dividir palavras; se você deseja armazenar uma lista de palavras em uma variável, deve usar uma matriz; e se você realmente deseja dividir palavras, pode escrever $=var.

files=(foo bar qux)
myprogram $files

Atualmente, os espaços nos nomes de arquivos são algo com o qual você precisa lidar, porque muitos usuários esperam que eles funcionem e porque muitos scripts são executados em contextos sensíveis à segurança, nos quais um invasor pode controlar os nomes dos arquivos. Portanto, a divisão automática de palavras costuma ser um incômodo; portanto, meu conselho geral de sempre usar aspas duplas, ou seja, escrever "$foo", a menos que você entenda por que precisa da divisão de palavras em um caso de uso específico. (Observe que expansões variáveis ​​simples também sofrem globbing.)

Gilles 'SO- parar de ser mau'
fonte
Obrigado Gilles, isso é realmente útil! É correto dizer que a divisão grosseira de palavras converte seqüências de caracteres do formulário "word1 word2 word3"em listas / matrizes do formulário "word1" "word2" "word3"? Também atualizei o OP com uma fonte específica de confusão no zsh.
Amelio Vazquez-Reina
11
@intrpc "Divisão de palavras" não é dividida em palavras da linguagem natural, mas em $IFScaracteres. Portanto, "divisão de campo" é um nome melhor. Mas "divisão de palavras" é freqüentemente usada para esse conceito na literatura de shell. A documentação do zsh está discutindo palavras.
Gilles 'SO- stop be evil'
11
Veja também rc(o shell plan9, também portado para Unix) para um design ainda melhor que o zsh quando se trata de variáveis ​​e matrizes.
Stéphane Chazelas
3

A divisão de palavras não é realmente específica do shell.

A maioria dos programas que precisam analisar a entrada de texto usa alguma forma de divisão de palavras como primeira etapa. Isso é feito antes da identificação dessas "palavras", números, operadores, strings, tokens e quaisquer entidades semelhantes que eles precisem processar.

O que é específico dos shells é que eles precisam criar adequadamente a lista de argumentos dos comandos chamados (C argc / argv, python sys.argv), incluindo a passagem de argumentos com espaços incorporados, argumentos vazios, delimitadores personalizados e assim por diante. Muitos shells usam a variável IFS para permitir alguma flexibilidade lá.

jlliagre
fonte
3

Nesse caso específico de Zsh, a divisão de palavras é definida de maneira ligeiramente diferente da divisão de campos.

Considere prog a b c, ele passará em três argumentos, não importa como você definir IFS. Isso é divisão de palavras .

Se o fizer A="a b c"; prog $A, ele passará em três argumentos, se IFSincluir espaço ou um argumento em contrário. Isso é divisão de campo .

As definições aqui são sutis. O que o documento Zsh está tentando dizer é que, mesmo se você desabilitar essa opção, prog a b cainda haverá argumentos separados (que é o que as pessoas sempre esperam).

Hot.PxL
fonte
11
Bart Schaefer, um desenvolvedor de zsh de longa data, confirma que esse é realmente o significado pretendido desse texto .
Stéphane Chazelas