¿Por qué a veces se necesita espacio en blanco alrededor de metacaracteres?

544

Hace unos meses me tatué una bomba tenedor en el brazo, y me salteé los espacios en blanco, porque creo que se ve mejor sin ellos. Pero para mi consternación, a veces (no siempre) cuando lo ejecuto en un shell no se inicia una bomba tenedor, pero solo da un error de sintaxis.

bash: syntax error near unexpected token `{:'

Ayer sucedió cuando intenté ejecutarlo en el shell Bash de un amigo , y luego agregué el espacio en blanco y de repente funcionó, en :(){ :|:& };:lugar de:(){:|:&};:

¿Importa el espacio en blanco? ¿He tatuado un error de sintaxis en mi brazo?

Parece que siempre funciona en zsh , pero no en Bash.

Una pregunta relacionada no explica nada acerca de los espacios en blanco, que realmente es mi pregunta; ¿Por qué se necesita el espacio en blanco para que Bash pueda analizarlo correctamente?

Spydon
fuente
66
Publiqué la misma pregunta aquí (excluyendo la parte del tatuaje).
Benoit
3
Además, los dos puntos (:) no se pueden usar como nombre de función (ver: pubs.opengroup.org/onlinepubs/9699919799/utilities/… ) ... FreeBSD's / bin / sh incluso da un error en esto ...
Martin Tournoij
55
@Carpetsmoker: no estoy seguro de cómo eso es relevante. Esta pregunta es sobre Bash.
Dennis

Respuestas:

268

Hay una lista de personajes que separan los tokens en BASH. Estos caracteres se llaman meta-caracteres y son |, &, ;, (, ), <, >, el espacio y la pestaña . Por otro lado, las llaves ( {y }) son solo caracteres ordinarios que forman palabras.

Omitir el segundo espacio antes }servirá, ya que &es un metacarácter. Por lo tanto, su tatuaje debe tener al menos un carácter espacial.

:(){ :|:&};:
Dmitri Chubarov
fuente
35
Solución fácil: mueva la primera parte del taoo hacia la izquierda y trasplante la piel entre las dos partes.
23
Me gustó el término, on the other hand... juego de palabras intencionado? ;): D (Lo siento, por el comentario fuera del tema.)
anishsane
44
Pero esto es diferente para zsh? ¿De qué manera es diferente zsh?
tfogo
2
Buena explicación, excepto eso {y }son palabras clave de shell en este contexto. Solo si no se reconocen como tales, debido a la falta de espacios / metacaracteres circundantes, se los trata como una parte literal de la palabra de la que se convierten. (Y luego está la expansión de
llaves
2
@DmitriChubarov Las llaves se permiten en los nombres de comandos (incluidas las funciones:. {foo} () { echo hello; }"Nombre", tal como se define bashcomo "una palabra que consiste solo en caracteres alfanuméricos y guiones bajos, y que comienza con un carácter alfabético o un guión bajo", se aplica solo a los nombres de variables.
chepner
82

Solo tatúa un

#!/bin/zsh

Shebang encima y estarás bien.

SzG
fuente
66
Si somos exigentes, el shebang no funcionaría en absoluto, ya que el shell, con suerte zsh, está en modo interactivo, como lo demuestra el aviso ...
SzG
50

Las llaves se parecen más a palabras clave extrañas que a símbolos especiales, y necesitan espacios. Esto es diferente a los paréntesis, por ejemplo. Comparar:

(ls)

que funciona y:

{ls}

que busca un comando llamado {ls}. Para trabajar, tiene que ser:

{ ls; }

El punto y coma detiene la llave de cierre que se toma como parámetro para ls.

Todo lo que tiene que hacer es decirle a la gente que está utilizando una fuente proporcional con un carácter de espacio bastante estrecho.

Peter Westlake
fuente
12
@DmitriChubarov: es un truco muy astuto, que utiliza un significado completamente diferente de los aparatos ortopédicos. Expande la lista de valores separados por comas, que en este caso es solo el ls.
Peter Westlake
77
Pero ... chico ... ¡vas a tener el tatuaje por el resto de tu vida! ¡Te marcaste como un nerd de todos los tiempos! ¿Y luego ni siquiera pudiste hacerlo bien antes de coserlo? Eso es dos pasos más allá de ser nerd, supongo ;-) Sin ofender, amigo, solo me pregunto.
Alfe
14
@Alfe Lo probé antes de tatuarlo, pero lo probé en mi zsh, pensé que tenía el mismo análisis que bash. Es tonto de mi parte, pero le diré a la gente que es zsh. :)
Spydon
20
@spydon O les dices que dejaste el espacio a propósito para que las personas que copian el comando de tu tatuaje no lo ejecuten accidentalmente y choquen su máquina;)
mete el
44
Oye, si es válido en zsh, ¿por qué no agregas #! / Bin / zsh justo encima (o antes)? Es una buena práctica especificar el shell primero, de todos modos.
Adam Miller
41

Aunque no es fácilmente visible en la fuente del tatuaje, en realidad hay una Marca de orden de bytes (BOM) entre el aparato ortopédico y el colon (es posible que haya estado lo suficientemente intoxicado cuando recibió el tatuaje que no lo notó, pero realmente está allí) . Esto deja tres posibilidades obvias:

  1. No ha podido escribir la lista de materiales cuando transcribió el código. El resultado es una aplicación obvia de GIGO. El shell simplemente no reconoce una lista de materiales que no está presente en su transcripción fallida.
  2. Tu caparazón es demasiado viejo. No reconoce los caracteres Unicode, por lo que la lista de materiales (y probablemente todos los demás caracteres Unicode) se ignora por completo, a pesar de que se supone que una lista de materiales en cualquier lugar que no sea el comienzo de un archivo debe tratarse como un espacio de ancho cero y sin interrupción .
  3. Tu caparazón es demasiado nuevo. El uso de una lista de materiales como ZWNBS está en desuso, y los autores han implementado una versión futura de Unicode en la que este uso ya no está permitido.
Jerry Coffin
fuente
40

y luego agregué el espacio en blanco y de repente funcionó ...

Es por la forma en que analiza el shell. Necesita un espacio después de que comience la definición de la función, es decir, después de {.

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

son validos. Por otra parte,

foo() {echo hey&}

no lo es


Realmente necesitas un tatuaje como este:

ingrese la descripción de la imagen aquí


De la fuente :

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

Omitir un espacio después de esto {hace {echoque se interprete como un token único.


Una forma equivalente de

:(){ :|:& };:

sería

:(){
:|:& };:

Tenga en cuenta que no hay espacio después {en la versión alternativa, pero un salto de línea hace que el shell se reconozca {como un token.

devnull
fuente
"Omitir un espacio después de {hace que el {echo se interprete como una sola ficha". - Entonces, ¿cree el analizador que ha encontrado un comando llamado {echoantes de que haya alcanzado una llave de apertura requerida?
Jonás el