Como o bash diferencia entre expansão de chaves e agrupamento de comandos?

47

Percebi que { pode ser usado na expansão de chaves:

echo {1..8}

ou no agrupamento de comandos:

{ls;echo hi}

Como o bash sabe a diferença?

    
por lovespring 10.01.2016 / 22:53

4 respostas

37

Um motivo simplificado é a existência de um caractere: space .

As expansões de chaves não processam espaços (não citados).

Uma lista {...} precisa de espaços (não citados).

A resposta mais detalhada é como o shell analisa um linha de comando .

O primeiro passo para analisar (entender) uma linha de comando é dividi-lo em partes.
Essas partes (geralmente chamadas de palavras ou tokens) resultam da divisão de uma linha de comando em cada meta-caractere do link :

  1. Splits the command into tokens that are separated by the fixed set of meta-characters: SPACE, TAB, NEWLINE, ;, (, ), <, >, |, and &. Types of tokens include words, keywords, I/O redirectors, and semicolons.

Meta-caracteres: espaço aba entra ; , < > | e & .

Após a divisão, as palavras podem ser de um tipo (conforme entendido pelo shell):

  • Pré-atribuições de comando: LC=ALL ...
  • Comando LC=ALL echo
  • Argumentos LC=ALL echo "hello"
  • Redirecionamento LC=ALL echo "hello" >&2

Expansão de órtese

Somente se uma "string de chave" (sem espaços ou metacaracteres) for uma única palavra (como descrito acima) e não for citada , ela é candidata a "expansão de contraventamento". Mais verificações são realizadas na estrutura interna depois.

Assim, este: {ls,-l} se qualifica como "Expansão de contraventamento" para se tornar ls -l , como first word ou argument (no bash, zsh é diferente).

$ {ls,-l}            ### executes 'ls -l'
$ echo {ls,-l}       ### prints 'ls -l'

Mas isso não será: {ls ,-l} . O Bash dividirá em espaço e analisará a linha como duas palavras: {ls e ,-l} , o que acionará um command not found (o argumento ,-l} é perdido):

 $ {ls ,-l}
 bash: {ls: command not found

Sua linha: {ls;echo hi} não se tornará uma "expansão de contraventamento" devido aos dois metacaracteres ; e espaço .

Ele será dividido em três partes: {ls new comando: echo hi} . Entenda que o ; aciona o início de um novo comando. O comando {ls não será encontrado e o próximo comando exibirá hi} :

$ {ls;echo hi}
bash: {ls: command not found
hi}

Se for colocado depois de algum outro comando, ele irá iniciar um novo comando após o ; :

$ echo {ls;echo hi}
{ls
hi}

Lista

Um dos "comandos compostos" é uma "Lista de Brace" (minhas palavras): { list; } .
Como você pode ver, ele é definido com espaços e um fechamento ; .
Os espaços e ; são necessários, porque { e } são "Reservados Palavras ".

E, portanto, para ser reconhecido como palavras, deve ser cercado por meta-caracteres (quase sempre: espaço ).

Conforme descrito no ponto 2 da página vinculada

  1. Checks the first token of each command to see if it is .... , {, or (, then the command is actually a compound command.

Seu exemplo: {ls;echo hi} não é uma lista.

Ele precisa de um fechamento ; e um espaço (pelo menos) depois de {. O último } é definido pelo fechamento ; .

Esta é uma lista { ls;echo hi; } . E esse { ls;echo hi;} também é (menos comumente usado, mas válido) (Obrigado @choroba pela ajuda).

$ { ls;echo hi; }
A-list-of-files
hi

Mas como argumento (o shell sabe a diferença) para um comando, ele dispara um erro:

$ echo { ls;echo hi; }
bash: syntax error near unexpected token '}'

Mas tenha cuidado com o que você acredita que o shell está analisando:

$ echo { ls;echo hi;
{ ls
hi
    
por 11.01.2016 / 04:39
16

O bloco { é uma palavra-chave shell, portanto, deve ser separado da próxima palavra por espaço, enquanto na expansão de chave, não deve haver espaço (se você precisar expandir um espaço, terá que escapar dele: echo {\ ,a}{b,c} ).

Você pode usar a expansão de contraventamento no início de um comando:

{ls,.}  # expands to "ls ."

Você não pode usá-lo para expandir para um bloco, embora a análise dos comandos de agrupamento ocorra antes das expansões:

echo {'{ ls','.;}'}  # { ls .;}
{'{ ls','.;}'}       # bash: { ls: No such file or directory
    
por 10.01.2016 / 22:58
5

Ele sabe, verificando a sintaxe da linha de comando. Da mesma forma, sabe que na expressão echo echo , o primeiro eco deve ser tratado como um comando e o segundo eco como um parâmetro do primeiro eco.

No bash é muito simples, pois { cmd; } deve ter espaços e ponto e vírgula. No entanto, por exemplo, em zsh eles não são necessários, mas ainda analisando o contexto de {} shell é capaz de dizer o que deve ser feito com seu conteúdo.

Considere o seguinte:

alias 1..3=date
{ 1..3; }    #in bash
{1..3}       #in zsh

Ambos retornam a data atual, mas

echo {1..3}

retorna 1 2 3 porque shell conhece {} em um argumento para o comando echo , portanto deve ser expandido.

    
por 10.01.2016 / 23:18
0

Em primeiro lugar, a chave composta deve ser uma palavra por si só e a primeira palavra da linha de comando:

echo { these braces are just words }

Em segundo lugar, chaves individuais não são especiais (como você pode ver acima). Chaves vazias também não são especiais:

echo {} # just the token {}: familiar from the find command

Qualquer coisa sem vírgulas também é apenas ela mesma

echo {abc} # just {abc}

Aqui é onde a ação começa.

echo {a,b} # braces disappear, a b results.

Então, basicamente para a expansão de contraventamento, precisamos de uma única palavra (não separada em campos no espaço em branco), dentro da qual ocorre pelo menos uma instância de {...} dentro da qual ocorre pelo menos uma vírgula. / p>

Esta lata pode ser a primeira palavra na linha de comando, a propósito:

{ls,-l} .   # just "ls -l ."
    
por 12.01.2016 / 01:06