Perché a volte gli spazi bianchi sono necessari intorno ai metacaratteri?


544

Alcuni mesi fa ho tatuato una bomba a forcella sul braccio e ho saltato gli spazi bianchi, perché penso che senza di loro sia più bello. Ma con mio sgomento, a volte (non sempre) quando lo eseguo in una shell non avvia una bomba a forcella, ma dà solo un errore di sintassi.

bash: syntax error near unexpected token `{:'

Ieri è successo quando ho provato a eseguirlo nella shell Bash di un amico , quindi ho aggiunto lo spazio bianco e all'improvviso ha funzionato, :(){ :|:& };:invece di:(){:|:&};:

Lo spazio bianco è importante; ho tatuato un errore di sintassi sul braccio ?!

Sembra funzionare sempre in zsh , ma non in Bash.

Una domanda correlata non spiega nulla degli spazi bianchi, che è davvero la mia domanda; Perché lo spazio bianco è necessario affinché Bash sia in grado di analizzarlo correttamente?


6
Ho pubblicato la stessa domanda qui (esclusa la parte del tatuaggio).
Benoit,

3
Inoltre, i due punti (:) non possono essere usati come nome di una funzione (vedi: pubs.opengroup.org/onlinepubs/9699919799/utilities/… ) ... Il file / bin / sh di FreeBSD fornisce anche un errore su questo ...
Martin Tournoij,

5
@Carpetsmoker: non sono sicuro di quanto sia rilevante. Questa domanda riguarda Bash.
Dennis,

Risposte:


268

C'è un elenco di personaggi che separano i token in BASH. Questi personaggi sono chiamati metacaratteri e sono |, &, ;, (, ), <, >, lo spazio e la scheda . D'altra parte, le parentesi graffe ( {e }) sono solo caratteri ordinari che compongono le parole.

Omettere il secondo spazio prima }farà, poiché &è un metacarattere. Pertanto, il tuo tatuaggio dovrebbe avere almeno un personaggio spaziale.

:(){ :|:&};:

35
Soluzione semplice: sposta la prima parte del taoo verso sinistra e trapianta la pelle tra le due parti.

23
Mi è piaciuto il termine, on the other hand... gioco di parole previsto? ;): D (Scusate, per il commento off topic.)
anishsane,

4
Ma questo è diverso per zsh? In che modo zsh è diverso?
tfogo,

2
Buona spiegazione, tranne che {e }sono parole chiave della shell in questo contesto. Solo se non vengono riconosciuti come tali - a causa della mancanza di spazi / metacaratteri circostanti - vengono trattati come una parte letterale della parola di cui diventano parte. (E poi c'è l'espansione del tutore, che accade in un contesto diverso.)
mklement0

2
Le parentesi graffe @DmitriChubarov sono consentite nei nomi dei comandi (comprese le funzioni:. {foo} () { echo hello; }"Nome", come definito da bash"una parola composta solo da caratteri alfanumerici e caratteri di sottolineatura e che inizia con un carattere alfabetico o un trattino basso", si applica solo ai nomi delle variabili.
Chepner,

82

Basta tatuaggio a

#!/bin/zsh

Shebang sopra di esso e starai bene.


6
Se siamo esigenti, lo shebang non funzionerebbe affatto, poiché la shell, si spera zsh, è in modalità interattiva, come evidenziato dal prompt ...
SzG

50

Le parentesi graffe sono più simili a parole chiave strane che a simboli speciali e hanno bisogno di spazi. Questo è diverso dalle parentesi, per esempio. Confrontare:

(ls)

che funziona e:

{ls}

che cerca un comando chiamato {ls}. Per funzionare, deve essere:

{ ls; }

Il punto e virgola interrompe il controvento di chiusura come parametro su ls.

Tutto quello che devi fare è dire alle persone che stai usando un carattere proporzionale con uno spazio piuttosto stretto.


12
@DmitriChubarov - è un trucco molto subdolo, che usa un significato completamente diverso di parentesi graffe. Espande l'elenco di valori separato da virgole, che in questo caso è solo il ls.
Peter Westlake,

7
Ma ... ragazzo ... avrai il tatuaggio per il resto della tua vita! Ti sei contrassegnato come un secchione per sempre! E poi non hai nemmeno potuto ottenerlo giusto prima di averlo cucito? Sono due passi oltre l'essere nerd immagino ;-) Senza offesa, amico, mi sto solo chiedendo.
Alfe,

14
@Alfe L'ho provato prima di tatuarlo, ma l'ho provato nel mio zsh, ho pensato che avesse lo stesso analisi di bash. Stupido da parte mia, ma dirò solo alla gente che è zsh. :)
Spydon,

20
@spydon Oppure dici loro che hai lasciato apposta lo spazio in modo che le persone che copiano il comando dal tuo tatuaggio non lo eseguano accidentalmente e si rompano la macchina;)
colpiscono il

4
Ehi, se è valido in zsh, perché non aggiungere semplicemente #! / Bin / zsh sopra di esso (o prima di esso)? È buona norma specificare prima la shell, comunque.
Adam Miller,

41

Sebbene non sia facilmente visibile nel tipo di carattere tatoo, tra il tutore e i due punti esiste in realtà un segno di byte-order (BOM) (potresti essere stato sufficientemente intossicato quando hai ottenuto il tatuaggio che non te ne sei accorto, ma è davvero lì) . Ciò lascia tre ovvie possibilità:

  1. Non hai digitato la DBA quando hai trascritto il codice. Il risultato è un'applicazione ovvia di GIGO. La shell semplicemente non riconosce una DBA che non è presente nella trascrizione fallita.
  2. La tua conchiglia è troppo vecchia. Non riconosce i caratteri Unicode, quindi la distinta base (e probabilmente tutti gli altri caratteri Unicode) viene ignorata completamente, anche se una distinta componenti ovunque ma l'inizio di un file dovrebbe essere trattata come uno spazio a larghezza zero non interrompibile .
  3. La tua shell è troppo nuova. L'uso di una distinta base come ZWNBS è obsoleto e gli autori hanno implementato una versione futura di Unicode in cui tale utilizzo non è più consentito.

40

e poi ho aggiunto lo spazio bianco e all'improvviso ha funzionato ...

È a causa di come la shell analizza. È necessario uno spazio dopo l'inizio della definizione della funzione, ovvero dopo {.

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

sono validi D'altro canto,

foo() {echo hey&}

non lo è.


In realtà hai bisogno di un tatuaggio come questo:

inserisci qui la descrizione dell'immagine


Dalla fonte :

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

L'omissione di uno spazio dopo il {causa l' {echointerpretazione come un singolo token.


Una forma equivalente di

:(){ :|:& };:

sarebbe

:(){
:|:& };:

Si noti che non c'è spazio dopo {nella versione alternativa, ma un'interruzione di linea fa sì che la shell riconosca {come token.


"L'omissione di uno spazio dopo {fa sì che l'eco {venga interpretato come un singolo token." - quindi il parser crede di aver incontrato un comando chiamato {echoprima che abbia raggiunto un controvento di apertura richiesto?
Giona il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.