Por que às vezes é necessário espaço em branco nos metacaracteres?

543

Há alguns meses, tatuei uma bomba de garfo no braço e pulei os espaços em branco, porque acho que fica melhor sem eles. Mas, para minha consternação, às vezes (nem sempre) quando eu o executo em um shell, ele não inicia uma bomba, mas apenas gera um erro de sintaxe.

bash: syntax error near unexpected token `{:'

Ontem aconteceu quando tentei executá-lo no shell Bash de um amigo e, em seguida, adicionei o espaço em branco e, de repente, funcionou, em :(){ :|:& };:vez de:(){:|:&};:

O espaço em branco importa; tatuei um erro de sintaxe no meu braço ?!

Parece sempre funcionar no zsh , mas não no Bash.

Uma pergunta relacionada não explica nada sobre os espaços em branco, o que realmente é minha pergunta; Por que o espaço em branco necessário para o Bash é capaz de analisá-lo corretamente?

Spydon
fonte
6
Eu postei a mesma pergunta aqui (excluindo a parte da tatuagem).
Benoit
3
Além disso, os dois pontos (:) não podem ser usados ​​como um nome de função (veja: pubs.opengroup.org/onlinepubs/9699919799/utilities/… ) ... O FreeBSD / bin / sh até dá um erro nisso ...
Martin Tournoij 24/01
5
@Carpetsmoker: Não sei como isso é relevante. Esta pergunta é sobre o Bash.
Dennis

Respostas:

267

Há uma lista de caracteres que separam os tokens no BASH. Esses personagens são chamados de metacaracteres e eles são |, &, ;, (, ), <, >, espaço e guia . Por outro lado, chavetas ( {e }) são apenas caracteres comuns que compõem palavras.

Omitir o segundo espaço antes }servirá, pois &é um metacaractere. Portanto, sua tatuagem deve ter pelo menos um caractere de espaço.

:(){ :|:&};:
Dmitri Chubarov
fonte
35
Solução fácil: mova a primeira parte do taoo para a esquerda e transplante a pele entre as duas partes.
23
Gostei do termo on the other hand... trocadilhos? ;): D (Desculpe, para fora do comentário tópico).
anishsane
4
Mas isso é diferente para o zsh? De que maneira o zsh é diferente?
tfogo
2
Boa explicação, exceto que {e }são palavras-chave shell neste contexto. Somente se não forem reconhecidos como tal - devido à falta de espaços / metacaracteres ao redor - eles serão tratados como uma parte literal da palavra da qual se tornam parte. (E então há expansão cinta, o que acontece num contexto diferente.)
mklement0
2
As chaves @DmitriChubarov são permitidas nos nomes de comando (incluindo as funções:. {foo} () { echo hello; }"Nome", como definido por bash"uma palavra que consiste apenas em caracteres alfanuméricos e sublinhados e que começa com um caractere alfabético ou sublinhado", aplica-se apenas a nomes de variáveis.
chepner
82

Apenas tatuar um

#!/bin/zsh

shebang acima e você vai ficar bem.

SzG
fonte
6
Se estamos exigente, o shebang não iria funcionar em tudo, como o shell, espero zsh, é no modo interativo, como evidenciado pelo prompt ...
Szg
50

Chaves são mais parecidas com palavras-chave ímpares do que símbolos especiais e precisam de espaços. Isso é diferente dos parênteses, por exemplo. Comparar:

(ls)

que funciona e:

{ls}

que procura um comando chamado {ls}. Para funcionar, tem que ser:

{ ls; }

O ponto-e-vírgula impede que a chave de fechamento seja usada como parâmetro para ls.

Tudo o que você precisa fazer é dizer às pessoas que você está usando uma fonte proporcional com um caractere de espaço bastante estreito.

Peter Westlake
fonte
12
@DmitriChubarov - é um truque muito sorrateiro, usando um significado completamente diferente de aparelho. Ele expande a lista de valores separados por vírgula, que neste caso é apenas o ls.
Peter Westlake
7
Mas ... garoto ... você vai fazer a tatuagem pelo resto da vida! Você se marcou como um nerd para sempre! E então você não conseguiu nem acertar antes de costurar? Isso é dois passos além de ser nerd, eu acho ;-) Sem ofensas, amigo, só estou pensando.
Alfe 17/01
14
@Alfe Eu tentei antes de tatuá-lo, mas tentei no meu zsh, achei que tinha a mesma análise do bash. Tolo da minha parte, mas vou apenas dizer às pessoas que é zsh. :)
spydon
20
@spydon Ou diga a eles que você deixou de fora o espaço de propósito, para que as pessoas que copiam o comando da sua tatuagem não o executem acidentalmente e bata na máquina deles;)
cutucam
4
Ei, se for válido no zsh, por que não adicionar #! / Bin / zsh logo acima (ou antes)? É uma boa prática especificar o shell primeiro, de qualquer maneira.
Adam Miller
41

Embora não seja facilmente visível na fonte da tatuagem, na verdade há uma BOM (marca de ordem de bytes) entre a chave e o cólon (você pode ter ficado intoxicado o suficiente quando recebeu a tatuagem que não notou, mas está realmente lá) . Isso deixa três possibilidades óbvias:

  1. Você não digitou a lista técnica ao transcrever o código. O resultado é uma aplicação óbvia do GIGO. O shell simplesmente não reconhece uma lista técnica que não está presente na transcrição que falhou.
  2. Seu shell é muito antigo. Ele não reconhece caracteres Unicode; portanto, a BOM (e provavelmente todos os outros caracteres Unicode) está sendo completamente ignorada, mesmo que uma BOM em qualquer lugar, exceto no início de um arquivo, seja tratada como um espaço sem quebra de largura zero. .
  3. Seu shell é muito novo. O uso de uma lista técnica como um ZWNBS foi descontinuado e os autores implementaram uma versão futura do Unicode na qual esse uso não é mais permitido.
Jerry Coffin
fonte
40

e então eu adicionei o espaço em branco e de repente funcionou ...

É por causa de como o shell analisa. Você precisa de um espaço após o início da definição da função, ou seja, após o {.

foo() { echo hey& }
foo() { echo hey&}
foo(){ echo hey&}

é válido. Por outro lado,

foo() {echo hey&}

não é.


Você realmente precisa de uma tatuagem como esta:

insira a descrição da imagem aqui


Da fonte :

  /* We ignore an open brace surrounded by whitespace, and also
     an open brace followed immediately by a close brace preceded
     by whitespace.  */

A omissão de um espaço após as {causas faz {echocom que seja interpretado como um único token.


Uma forma equivalente de

:(){ :|:& };:

seria

:(){
:|:& };:

Observe que não há espaço depois {na versão alternativa, mas uma quebra de linha faz com que o shell seja reconhecido {como um token.

devnull
fonte
"Omitir um espaço após o {faz com que o eco seja interpretado como um único token." - o analisador acredita que encontrou um comando chamado {echoantes de atingir a chave de abertura necessária?
Jonah