¡Cómo hacer eco de una explosión!


59

Traté de crear un script echointroduciendo el contenido en un archivo, en lugar de abrirlo con un editor

echo -e "#!/bin/bash \n /usr/bin/command args"  > .scripts/command

La salida :

bash:! / bin / bash: evento no encontrado

He aislado este extraño comportamiento a la explosión .

$ echo !
!  

$ echo "!"
bash: !: event not found

$ echo \#!
#!

$ echo \#!/bin/bash
bash: !/bin/bash: event not found
  • ¿Por qué la explosión está causando esto?
  • ¿Cuáles son estos "eventos" a los que se refiere bash?
  • ¿Cómo puedo superar este problema e imprimir "#! / Bin / bash" en la pantalla o en mi archivo?

Respuestas:


59

Intenta usar comillas simples.

echo -e '#!/bin/bash \n /usr/bin/command args'  > .scripts/command

echo '#!'

echo '#!/bin/bash'

El problema se produce porque bash está buscando en su historial! / Bin / bash. El uso de comillas simples escapa a este comportamiento.


3
También deshabilita la interpolación de cadenas :(
Hubro

¡Ese es el punto! Puede agregar otros argumentos al mismo comando echo usando comillas dobles y obtener interpolación en esas partes.
Richm

3
También puede usar una cadena de estilo C en bash con $''. Por ejemplo, echo $'1!\n2!\n3!\n'imprime cada número seguido de una explosión y una nueva línea.
spelufo

1
Poner bang entre comillas simples no me ayudó al ingresar una cadena de varias líneas:! bash: las comillas simples no escapan a la explosión (de verdad)
binki

1
@kbolino Tienes razón. Entonces, la cadena de ejemplo completa sería:, echo '0123'"$var"'456'observe dos pares de comillas simples y un par de comillas dobles.
phyatt

16

Como Richm dijo , bash está tratando de hacer un partido de historia . Otra forma de evitarlo es escapar de la explosión con un \:

$ echo \#\!/bin/bash
#!/bin/bash

Aunque tenga cuidado con las comillas dobles, \no se elimina:

$ echo "\!"
\!

8
En bash, en "\!"realidad se expande \!, no !, supuestamente para el cumplimiento de POSIX.
Mikel

@Mikel suspiro. Tantas diferencias entre zsh y bash
Michael Mrozek

Esto funciona incluso al ingresar una cadena multilínea. Recordar que 1. estar fuera de una cadena al ingresar la explosión y 2. usar este método (barra invertida) parece funcionar con la menor sorpresa en la mayoría de los escenarios.
binki

1
Sería útil tener en cuenta que bash no realiza búsquedas en el historial al ejecutar un script, por lo que no es necesario hacer nada especial allí.
binki

¿No hay sintaxis para escapar simplemente !entre comillas dobles sin comportamiento mágico? Esto es una locura ...
Lassi

13

!inicia una sustitución del historial (un "evento" es una línea en el historial del comando); por ejemplo se !lsexpande a la última línea de comando que contiene ls, y se !?fooexpande a la última línea de comando que contiene foo. También puede extraer palabras específicas (por ejemplo, se !!:1refiere a la primera palabra del comando anterior) y más; Vea el manual para más detalles.

Esta característica se inventó para recuperar rápidamente los comandos anteriores en los días en que la edición de la línea de comandos era primitiva. Con los shells modernos (al menos bash y zsh) y copiar y pegar, la expansión del historial ya no es tan útil como solía serlo, sigue siendo útil, pero puede sobrevivir sin ella.

Puede cambiar qué carácter desencadena la sustitución del historial configurando la histcharsvariable; si rara vez usa la sustitución del historial, puede configurar, por ejemplo, histchars='¡^'para que active la ¡expansión del historial en lugar de !. Incluso puede desactivar la función por completo con set +o histexpand.


3

Para poder deshabilitar la expansión del historial en una línea de comando en particular, puede usar spacecomo el tercer carácter de $histchars:

histchars='!^ '

Luego, si ingresa su comando con un espacio inicial, no se realizará la expansión del historial.

bash-4.3$ echo "#!/bin/bash"
bash: !/bin/bash: event not found
bash-4.3$  echo "#!/bin/bash"
#!/bin/bash

Sin embargo, tenga en cuenta que los espacios iniciales también se usan cuando $HISTCONTROLcontiene ignorespacecomo una forma de decirle que bashno registre una línea de comando en el historial.

Si desea ambas funciones de forma independiente, deberá elegir otro personaje como el tercer personaje de $histchars. Desea uno que no afecte la forma en que se interpreta su comando. Algunas opciones:

  • usando la barra invertida ( \): \echo foofunciona pero tiene el efecto secundario de deshabilitar los alias o las palabras clave.
  • TAB: sin embargo, para insertarlo en la primera posición, debe presionar Ctrl+VTab.
  • si no le importa escribir dos teclas, se puede elegir cualquier personaje que normalmente no aparece en primera posición ( %, @, ?, escoger su propio) y crea un alias de vacío para ello:

    histchars='!^%'
    alias %=
    

    Luego ingrese ese carácter, espacio y su comando:

    bash-4.3$ % echo !!
    !!
    

(no podrá registrar un comando donde la sustitución del historial ha sido desactivada. Tenga en cuenta también que el tercer carácter predeterminado de $histcharses #para que la expansión del historial no se realice en los comentarios. Si lo cambia e ingresa comentarios en el pronto, debe tener en cuenta el hecho de que las secuencias! pueden expandirse allí).


2

Las soluciones propuestas no funcionan, por ejemplo, en el siguiente ejemplo:

$ bash -c "echo 'hello World!'"
-bash: !'": event not found
$

En este caso, la explosión se puede imprimir usando su código octal ASCII:

$ bash -c "echo -e 'hello World\0041'"
hello World!
$

1
En su primer comando, !está entre comillas dobles, donde, como indican otras respuestas, conserva su significado de expansión de historial. Podrías usar bash -c 'echo '\''hello World!'\'o bash -c "echo 'hello World"\!"'".
Gilles 'SO- deja de ser malvado'

Sin el parámetro -i, el entorno puede verse alterado (como si su ~ / .profile no se ejecute). Sin embargo, hacer -i significa que cualquier salida de su .profile se capturará en la salida del comando que realmente desea ejecutar.
Brent

-1

Desde Bash 4.3, ahora puede usar comillas dobles para citar el carácter de expansión del historial:

$ bash --version
GNU bash, version 4.3...
[...]
$ echo "Hello World!"
Hello World!

44
No, eso solo es !seguido por "eso ya no es especial. echo "foo!"está bien, echo "foo!bar"no lo está
Stéphane Chazelas
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.