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 do shell POSIX define o grupo de chaves e o subshell da seguinte maneira
brace_group : Lbrace compound_list Rbrace
subshell : '(' compound_list ')'
Agora, se estivermos lendo isso literalmente, os espaços são significativos. Isso significaria que deve haver espaço delimitando abertura e fechamento de chave e parênteses como em
{ echo hello world; }
( echo hello world )
Isso também se alinharia com as definições do Comando Composto :
Cada um desses comandos compostos tem uma palavra reservada ou operador de controle no início e uma palavra reservada ou operador de terminação correspondente no final.
No entanto, o que não faz sentido é porque (list)
e ( list )
funciona bem (esse espaço depois (
não é necessário), no entanto, a expansão do brace tem que ter um espaço à esquerda, 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 ao conceito de divisão de campo , no entanto, a própria definição não faz menção a espaços. Além disso, se {
e (
ambos são considerados palavras reservadas pela definição POSIX do comando composto, por que eles são tratados 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 sequê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 {
é considerado um "token", onde as (
não é 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 resposta aceitas:
Movida a marca de seleção aceita para a resposta de Isaac, pois fornece uma citação do próprio padrão 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;}, de modo que, neste caso, o e principal<space>
e<semicolon>
são obrigatórios.Aceitando a resposta de Kusalananda. A resposta de Kusalananda atende ao que eu precisava, embora principalmente do ponto de vista informal e intuitivo; aponta{
é 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 tem uma palavra reservada ou operador de controle no início
{
são definidos como palavras reservadas, semelhantes afor
ouwhile
, listadas na Shell Grammar (veja o último bloco de código na pergunta)A seção 2.9 declara (ênfase adicionada):
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 um operador,(
é referido como operador; especificamente, a seção 2.9.2 dizSe o pipeline começar com a palavra reservada ! e command1 é um comando de subshell, o aplicativo deve garantir que o operador ( no início de command1 seja separado do ! por um ou mais caracteres. O comportamento da palavra reservada ! imediatamente seguido pelo operador ( não é especificado.
A pergunta sobre o Stack Overflow por Digital Trauma aponta a Seção 2.4 sobre Palavras Reservadas:
Este reconhecimento só ocorrerá quando nenhum dos caracteres for citado e quando a palavra for utilizada 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 lá nos dados de entrada do shell, mas apenas uma maneira de exibir a própria gramática. É o fato de as chaves serem 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 só, eles precisariam ser listados na produção "
Caso encerrado.
A diferença entre as chaves e os parênteses é que as chaves (e
!
) são palavras reservadas, assim comofor
,if
,then
etc. enquanto os parênteses são operadores de controle. As palavras precisam ser separadas por espaço em branco.Isso significa que assim como você não pode ter
você não pode ter
ou
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 as chaves são palavras reservadas que implica que elas devem ser cercadas por espaços em branco, enquanto os parênteses de um subshell não.
Essa é uma limitação da maneira como o shell quebra as linhas em tokens.
O shell lê as linhas do arquivo de entrada e de acordo com a seção 2 "Introdução do Shell" as converte em uma palavra ou um operador :
{ é uma palavra reservada
Algumas palavras são palavras reservadas
As palavras, para serem reconhecidas como palavras, devem ser delimitadas .
Principalmente por espaços em branco (ponto 7) e por operadores.
(é um operador
Os operadores ficam sozinhos :
Onde "operadores" são :
Os operadores de redirecionamento são :
Os operadores de controle são :
Conclusão
Assim, '(' 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 :
O que explica exatamente por que um espaço (ou algum outro delimitador) é necessário após um
{
.Isso é válido:
Como é este:
Este:
Ou mesmo isso: