Pourquoi faut-il parfois des espaces autour des métacaractères?

544

Il y a quelques mois, j'ai tatoué une bombe à fourche sur mon bras et j'ai sauté les espaces blancs, car je pense que c'est plus joli sans eux. Mais à ma grande consternation, parfois (pas toujours) lorsque je l'exécute dans un shell, il ne démarre pas une bombe fourchette, mais il donne juste une erreur de syntaxe.

bash: syntax error near unexpected token `{:'

Hier, cela s'est produit lorsque j'ai essayé de l'exécuter dans le shell Bash d' un ami , puis j'ai ajouté un espace et cela a soudainement fonctionné, :(){ :|:& };:au lieu de:(){:|:&};:

L'espace est-il important? ai-je tatoué une erreur de syntaxe sur mon bras?!

Il semble toujours fonctionner dans zsh , mais pas dans Bash.

Une question connexe n'explique rien sur les espaces blancs, ce qui est vraiment ma question; Pourquoi l'espace blanc est-il nécessaire à Bash pour pouvoir l'analyser correctement?

spydon
la source
6
J'ai posté la même question ici (à l'exception de la partie tatouage).
Benoit
3
De plus, les deux points (:) ne peuvent pas être utilisés comme nom de fonction (voir: pubs.opengroup.org/onlinepubs/9699919799/utilities/… ) ... FreeBSD / bin / sh donne même une erreur à ce sujet ...
Martin Tournoij
5
@Carpetsmoker: Je ne sais pas en quoi c'est pertinent. Cette question concerne Bash.
Dennis

Réponses:

268

Il y a une liste de caractères qui séparent les jetons dans BASH. Ces caractères sont appelés métacaractères et ils sont |, &, ;, (, ), <, >, espace et onglet . D'un autre côté, les accolades ( {et }) ne sont que des caractères ordinaires qui composent les mots.

Omettre le deuxième espace avant }fera l'affaire, car &c'est un métacaractère. Par conséquent, votre tatouage doit avoir au moins un caractère d'espace.

:(){ :|:&};:
Dmitri Chubarov
la source
35
Solution facile: déplacez la première partie du taoo vers la gauche et transplantez la peau entre les deux parties.
23
J'ai aimé le terme, on the other hand... jeu de mots voulu? ;): D (Désolé, pour le commentaire hors sujet.)
anishsane
4
Mais c'est différent pour zsh? En quoi zsh est-il différent?
tfogo
2
Bonne explication, sauf que {et }sont des mots clés shell dans ce contexte. Ce n'est que s'ils ne sont pas reconnus comme tels - en raison du manque d'espaces / métacaractères environnants - qu'ils sont traités comme une partie littérale du mot dont ils deviennent une partie. (Et puis il y a l'expansion de l'accolade, qui se produit dans un contexte différent.)
mklement0
2
@DmitriChubarov Les accolades sont autorisées dans les noms de commande (y compris les fonctions:. {foo} () { echo hello; }"Nom", tel que défini par bashpar "un mot composé uniquement de caractères alphanumériques et de soulignements, et commençant par un caractère alphabétique ou un soulignement", s'applique uniquement aux noms de variables.
chepner
82

Il suffit de tatouer un

#!/bin/zsh

shebang dessus et vous irez bien.

SzG
la source
6
Si nous sommes pointilleux, le shebang ne fonctionnerait pas du tout, car le shell, espérons-le zsh, est en mode interactif, comme en témoigne l'invite ...
SzG
50

Les accolades ressemblent plus à des mots-clés impairs qu'à des symboles spéciaux et ont besoin d'espaces. C'est différent des parenthèses, par exemple. Comparer:

(ls)

qui fonctionne, et:

{ls}

qui recherche une commande nommée {ls}. Pour fonctionner, il faut:

{ ls; }

Le point-virgule empêche l'accolade fermante d'être prise comme paramètre pour ls.

Tout ce que vous avez à faire est de dire aux gens que vous utilisez une police proportionnelle avec un caractère d'espace assez étroit.

Peter Westlake
la source
12
@DmitriChubarov - c'est une astuce très sournoise, utilisant une signification complètement différente des accolades. Il étend la liste de valeurs séparées par des virgules, qui dans ce cas est juste la ls.
Peter Westlake
7
Mais… mon garçon… tu vas avoir le tatouage pour le reste de ta vie! Vous vous êtes marqué comme un nerd pour toujours! Et puis, vous ne pouviez même pas le faire correctement avant de l'avoir cousu? C'est deux étapes au-delà d'être ringard, je suppose ;-) Aucune infraction, mon pote, je me demande juste.
Alfe
14
@Alfe Je l'ai essayé avant de le tatouer, mais je l'ai essayé dans mon zsh, je pensais qu'il avait la même analyse syntaxique que bash. Insensé de moi, mais je vais juste dire aux gens que c'est zsh. :)
spydon
20
@spydon Ou vous leur dites que vous avez délibérément laissé de l'espace pour que les gens copiant la commande de votre tatouage ne l'exécutent pas accidentellement et ne plantent pas leur machine;)
piquez le
4
Hé, si c'est valide dans zsh, pourquoi ne pas simplement ajouter #! / Bin / zsh juste au-dessus (ou avant)? Il est recommandé de spécifier le shell en premier, de toute façon.
Adam Miller,
41

Bien qu'il ne soit pas facilement visible dans la police tatoo, il y a en fait une marque d'ordre des octets (BOM) entre l'accolade et le colon (vous avez peut-être été suffisamment intoxiqué lorsque vous avez reçu le tatouage que vous ne l'avez pas remarqué, mais il est vraiment là) . Cela laisse trois possibilités évidentes:

  1. Vous n'avez pas réussi à saisir la nomenclature lorsque vous avez transcrit le code. Le résultat est une application évidente de GIGO. Le shell ne reconnaît tout simplement pas une nomenclature qui n'est pas présente dans votre transcription ayant échoué.
  2. Votre coquille est trop vieille. Il ne reconnaît pas les caractères Unicode, donc la nomenclature (et probablement tous les autres caractères Unicode) est complètement ignorée, même si une nomenclature n'importe où, mais le début d'un fichier est censé être traité comme un espace insécable de largeur nulle .
  3. Votre coquille est trop neuve. L'utilisation d'une nomenclature en tant que ZWNBS est déconseillée et les auteurs ont implémenté une future version d'Unicode dans laquelle cette utilisation n'est plus autorisée.
Jerry Coffin
la source
40

et puis j'ai ajouté le blanc et ça a soudainement fonctionné ...

C'est à cause de la façon dont le shell analyse. Vous avez besoin d'un espace après le début de la définition de la fonction, c'est-à-dire après le {.

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

sont valides. D'autre part,

foo() {echo hey&}

n'est pas.


Vous avez en fait besoin d'un tatouage comme celui-ci:

entrez la description de l'image ici


De la source :

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

L'omission d'un espace après le {fait que le {echosoit interprété comme un seul jeton.


Une forme équivalente de

:(){ :|:& };:

serait

:(){
:|:& };:

Notez qu'il n'y a pas d'espace après {dans la version alternative, mais un saut de ligne fait reconnaître le shell {comme un jeton.

devnull
la source
"L'omission d'un espace après le {fait que l'écho {soit interprété comme un seul jeton." - ainsi, l'analyseur pense-t-il qu'il a rencontré une commande appelée {echoavant d'avoir atteint une accolade d'ouverture requise?
Jonah