Por que um grupo de comandos de chaves precisa de espaços após a chave de abertura na POSIX Shell Grammar?

10

TL; DR : Por que o grupo de chaves POSIX precisa de espaços após a {palavra reservada, mas o subshell não após a palavra reservada (?

A gramática de shell POSIX define o grupo de chaves e o subshell da seguinte maneira

brace_group      : Lbrace compound_list Rbrace

subshell         : '(' compound_list ')'

Agora, se estamos lendo isso literalmente, os espaços são significativos. Isso significa que deve haver espaço delineando a chave de abertura e fechamento e o parêntese, como

{ echo hello world; }

( echo hello world )

Isso também se alinharia às definições de comando composto :

Cada um desses comandos compostos possui uma palavra reservada ou operador de controle no início e uma palavra ou operador reservado terminador correspondente no final.

No entanto, o que não faz sentido é o porquê (list)e ( list )funciona muito bem (esse espaço depois (não é necessário), no entanto, a expansão da cinta precisa ter um espaço à frente, ou seja {echo hello;}, não funcionaria.

É claro que a palavra reservada sendo tratada como palavra shell faria sentido precisar de um espaço depois para se alinhar com o conceito de divisão de campos , no entanto, a própria definição não faz menção de espaços. Além disso, se {e (são consideradas palavras reservadas pela definição POSIX de comando composto, por que são tratadas de maneira diferente em relação ao caractere de espaço após essas palavras reservadas? Agora, o manual do ksh (1) declara:

As palavras, que são seqüências de caracteres, são delimitadas por caracteres de espaço em branco sem aspas (espaço, tabulação e nova linha) ou metacaracteres (<,>, |,;, &, (e))

Em outras palavras, faz sentido que o ksh reconheça (como delimitador de palavras, onde a primeira palavra seria um comando ou atribuição de variável. POSIX, no entanto, não parece ser mencionado (como meta-caractere. A única explicação possível que encontrei no que diz respeito à gramática POSIX é que ela {é considerada um "token", onde (não está listado como um.

/* These are reserved words, not operator tokens, and are
   recognized when reserved words are recognized. */


%token  Lbrace    Rbrace    Bang
/*      '{'       '}'       '!'   */

Então, qual seria o raciocínio preciso para essa discrepância?

Notas de respostas aceitas:

  • Movi a marca de seleção aceita para a resposta de Isaac, pois fornece ao formulário o padrão em si que aborda diretamente minha pergunta:

    Por exemplo, '(' e ')' são operadores de controle, de modo que não <space>é necessário em (lista). No entanto, '{' e '}' são palavras reservadas em {list;}, portanto, neste caso, as iniciais <space>e <semicolon>são obrigatórias.

  • Aceitando a resposta de Kusalananda . A resposta de Kusalananda aborda o que eu precisava, embora principalmente do ponto de vista informal e intuitivo; indica que {é uma palavra reservada e (é operador. Michael Homer também observou o mesmo nos comentários - que a definição do Comando Composto afirma (ênfase adicionada):

    Cada um desses comandos compostos possui uma palavra reservada ou operador de controle no início

  • {são definidos como palavras reservadas, semelhantes forou whilelistadas na Shell Grammar (consulte o último bloco de código na pergunta)

  • A Seção 2.9 declara (grifo nosso):

    Em particular, as representações incluem espaçamento entre tokens em alguns lugares onde <blank>s não seria necessário (quando um dos tokens é um operador).

  • Enquanto o padrão não define explicitamente (como operador, (é referido como operador; especificamente, a seção 2.9.2 diz

    Se o pipeline começar com a palavra reservada! e command1 é um comando subshell, o aplicativo deve garantir que o operador (no início do comando1 seja separado do! por um ou mais caracteres. O comportamento da palavra reservada! imediatamente seguido pelo operador (não é especificado.

  • Pergunta sobre Stack Overflow by Digital Trauma indica a Seção 2.4 em Palavras reservadas:

    Este reconhecimento deve ocorrer apenas quando nenhum dos caracteres é citado e quando a palavra é usada como:

    -A primeira palavra de um comando

  • Como mencionado na resposta de Kusalananda "Os espaços mostrados na gramática POSIX não são espaços que precisam estar nos dados de entrada do shell, mas apenas uma maneira de exibir a gramática em si. É o fato de que os aparelhos são palavras reservadas que implica que eles precisam ser cercados por espaços em branco "Como mencionado por Michael Homer nos comentários:" Se os espaços fossem significativos por si mesmos, eles precisariam ser listados na produção "

Caso encerrado.

Sergiy Kolodyazhnyy
fonte
3
Se os espaços fossem significativos por si só, eles precisariam ser listados na produção.
Michael Homer
2
"Além disso, se {e (são considerados palavras reservadas pela definição POSIX de comando composto", cf. "Cada um desses comandos compostos possui uma palavra reservada ou operador de controle no início".
Michael Homer
2
@SergiyKolodyazhnyy Acredito que ele significa que, se o espaço fosse significativo, a gramática teria que incluir um caractere de espaço explícito ( ' '). Em vez disso, os espaços estão implícitos em quais símbolos são palavras.
Kusalananda
2
A definição de especificação da classe de token é ... estranha, para dizer o mínimo. A gramática inteira é bastante terrível e as especificações misturam coisas definidoras na prosa no texto (às vezes implicitamente!), Nas regras da prosa que precedem a gramática e na própria gramática. É bastante incompreensível se você ainda não sabe a resposta e trabalha de trás para frente. As regras lexicais são todas definidas ao contrário, pelo que inicia um novo token, em vez de descrever o que o token contém. É apenas uma bagunça por toda parte.
Michael Homer
1
@Sergiy na gramática formal, uma produção (ou regra de produção) descreve como você pode gerar algo a partir de outra coisa. Consulte en.wikipedia.org/wiki/Production_%28computer_science%29 Assim como command : simple_command | compound_command | compound_command redirect_list | function_definition ;é uma produção que diz onde você pode ter um comando, pode ser um comando simples, comando composto ou comando composto com redirecionamento ou definição de função.
Muru

Respostas:

6

Essa é uma limitação da maneira como o shell quebra linhas em tokens.

O shell lê as linhas do arquivo de entrada e, de acordo com a seção 2 "Introdução ao Shell", as converte em uma palavra ou em um operador :

  1. O shell divide a entrada em tokens: palavras e operadores

{é uma palavra reservada

Algumas palavras são palavras reservadas

Palavras reservadas são aquelas que têm um significado especial para o shell. As seguintes palavras devem ser reconhecidas como palavras reservadas:

! { } case do done elif else esac fi for if in then until while

As palavras, para serem reconhecidas como palavras, devem ser delimitadas .

As palavras reservadas são reconhecidas apenas quando são delimitadas ...

Principalmente por espaços em branco (ponto 7) e pelos operadores.

  1. Se o caractere atual for um <blank> sem aspas, qualquer token que contenha o caractere anterior será delimitado e o caractere atual será descartado.

(é um operador

Os operadores se destacam :

enquanto operadores são eles próprios delimitadores.

Onde "operadores" são :

3.260 Operator

Na linguagem de comando do shell, um operador de controle ou um operador de redirecionamento .

Os operadores de redirecionamento são :

Operador de redirecionamento

Na linguagem de comando do shell, um token que executa uma função de redirecionamento. É um dos seguintes símbolos:

<     >     >|     <<     >>     <&     >&     <<-     <>

Os operadores de controle são :

3.113 Operador de controle

Na linguagem de comando do shell, um token que executa uma função de controle. É um dos seguintes símbolos:

&   &&   (   )   ;   ;;   newline   |   ||

Conclusão

Portanto, '(' e ')' são operadores de controle, enquanto '{' '}' são palavras reservadas.

E a mesma descrição exata da sua pergunta está dentro da especificação :

Por exemplo, '(' e ')' são operadores de controle, de modo que nenhum <espaço> é necessário em (lista). No entanto, '{' e '}' são palavras reservadas em {list;}, de modo que, neste caso, os <espaço> e <semicolon> iniciais são necessários.

O que explica exatamente por que um espaço (ou outro delimitador) é necessário após a {.

Isso é válido:

{ echo yes;}

Como é isso:

{(echo yes);}

Este:

{(echo yes)}

Ou até isso:

{>/dev/tty echo yes;}
Isaac
fonte
Bem, a última citação é exatamente exata! Marcado com +1. Vou precisar rever a pergunta e as respostas agora #
Sergiy Kolodyazhnyy
13

A diferença entre as chaves e os parênteses são de que as chaves (e !) são palavras reservadas, assim como for, if, thenetc., enquanto parênteses são operadores de controle. As palavras precisam ser separadas por espaços em branco.

Isso significa que, assim como você não pode ter

foriin*; do

você não pode ter

{somecommand;} >file

ou

if !somecommand; then

Os espaços mostrados na gramática POSIX não são espaços que precisam estar presentes nos dados de entrada do shell, mas apenas uma maneira de exibir a própria gramática. É o fato de os chavetas serem palavras reservadas que implica que elas precisam ser cercadas por espaços em branco, enquanto os parênteses de um subshell não.

Kusalananda
fonte
1
Bem, isso parece responder e eu vejo que diz "Em particular, as representações incluem espaçamento entre tokens em alguns lugares onde os <blank> s não seriam necessários (quando um dos tokens é um operador)". Apenas uma pergunta: onde o padrão define (como operador? Não é na seção de gramática, pelo menos
Sergiy Kolodyazhnyy
@MichaelHomer Ah, "operador de controle", assim como ;. Obrigado por isso.
Kusalananda
Os operadores de controle estão listados na parte superior da página do manual em DEFINIÇÕES. Podemos considerar ()operadores de controle como os |que envolvem subcascas. E { }funciona no shell atual e não pode envolver um subshell.
Glenn Jackman
@Kusalananda Encontrou, seção 2.9.2: "Se o pipeline começar com a palavra reservada! E command1 for um comando subshell, o aplicativo deve garantir que o operador (no início do command1 seja separado do! Por um ou mais < .! blank> caracteres o comportamento da palavra reservada imediatamente seguido pela (operador é indeterminado "Não é uma definição clara, mas o padrão não chamá-lo. (operador
Sergiy Kolodyazhnyy
@glennjackman Embora seja verdade que os pipelines envolvem subcascas, esse não é o tipo de definição que parece apropriado. O padrão também menciona que, em algumas implementações, não há problema em o pipeline ser executado no ambiente de execução de shell atual (e eu sei que ele está no padrão, porque vi o texto ontem e o procurei agora). No entanto, a sua sugestão ponto de me encontrar a citação eu comentei acima, em que pelo menos o padrão faz chamar -lo operador embora não explicitamente definir isso como um
Sergiy Kolodyazhnyy