Qu'est-ce que “-bash:!”: Événement non trouvé "

114

Essayez d’exécuter ce qui suit sous un shell bash echo "Reboot your instance!"

Sur mon installation:

root@domU-12-31-39-04-11-83:/usr/local/bin# bash --version
GNU bash, version 4.1.5(1)-release (i686-pc-linux-gnu)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
root@domU-12-31-39-04-11-83:/usr/local/bin# uname -a
Linux domU-12-31-39-04-11-83 2.6.35-22-virtual #35-Ubuntu SMP Sat Oct 16 23:57:40 UTC 2010 i686 GNU/Linux
root@domU-12-31-39-04-11-83:/usr/local/bin# echo "Reboot your instance!"
-bash: !": event not found

Quelqu'un peut-il s'il vous plaît expliquer ce que sont les "événements bash?" Je n'ai jamais entendu ce concept auparavant. Aussi, comment devrais-je sortir "!" à la fin de la phrase?

Maxim Veksler
la source

Réponses:

53

Vous pouvez désactiver la substitution d'historique à l'aide de set +H.

Dennis Williamson
la source
2
Connaissez-vous le raisonnement derrière la syntaxe? Cela semble vraiment contre-intuitif. Vous penseriez taper +Hpour activer quelque chose et -Hdésactiver quelque chose.
Gavin Ward
7
@PunkyGuy: Je ne connais pas l'historique derrière le jeu de mots, mais les options Unix commencent généralement par un trait d'union (ou moins) et +sont tout simplement le contraire.
Dennis Williamson
105

!est un caractère spécial à bash, il est utilisé pour faire référence aux commandes précédentes; par exemple,

!rm

va rappeler et exécuter la dernière commande qui a commencé avec la chaîne "rm", et

!rm:p

rappellera mais n'exécutera pas la dernière commande qui a commencé par la chaîne "rm". bash interprète le point d'exclamation echo "reboot your instance!"comme " remplacez ici la dernière commande qui a commencé par le ou les caractères suivant immédiatement le point d'exclamation ", et vous grogne de ne pas trouver un événement (commande) dans votre historique commençant par un simple double citation.

Essayer

echo reboot your instance\!

protéger (échapper) le point d'exclamation de bash.

Chapelier Fou
la source
Oui, mais cela devrait arriver si je veux utiliser echo -e "du texte $ ENV_VAL un peu plus de texte \ nReboot maintenant!" ?
Maxim Veksler
1
Écrivez deux déclarations d'écho; vous recevez le saut de ligne gratuitement.
MadHatter
1
La solution simple consiste à utiliser des guillemets simples et des guillemets doubles à la fois: echo -e "Text \ nReeboot" '!'
mmey
4
Ça craint. Si je n'échappe pas au point d'exclamation, bash me fait mal. Si je fais échapper, la barre oblique inverse est inclus dans la chaîne finale. Pourquoi faites-vous cela, Bash !! ( pastebin.com/g4PYv56A )
Hubro le
2
@Hubro en toute justice, je ne suis pas sûr que tout soit de la faute de bash. Vous avez quelque chose de compliqué à utiliser le rubis pour alimenter des choses à critiquer, et je ne vois pas très bien quel rôle joue le rubis dans l'élimination ou le renforcement des personnages de l'évasion.
MadHatter
25

Pour résoudre votre problème initial, essayez d'utiliser des guillemets simples plutôt que des guillemets doubles. Avec ce dernier, bash tentera de développer certains caractères avant de transmettre le résultat à la commande (écho dans ce cas). Avec des guillemets simples, bash passe la chaîne entière sans aucune modification.

!est utilisé dans les commandes pour faire référence à l'historique de ligne de commande. Voir: http://tldp.org/LDP/abs/html/abs-guide.html#HISTCOMMANDS pour un ensemble complet. Avec l'exemple ci-dessus, bash tente de se développer en !"tant que référence à un événement avant que l'écho ne soit examiné, d'où l'erreur.

Notez que dans les scripts, toutes les commandes d'historique sont désactivées, car elles n'ont de sens que dans un shell interactif.

Le seul que j'utilise régulièrement, c'est !$. Il se développe jusqu'au dernier argument de la commande précédente. C'est un raccourci utile dans certains endroits.

SmallClanger
la source
4
!!remplir la dernière commande complète entrée est remarquablement utile; en particulier, sudo !!oupfexec !!
jgoldschrafe
2
Je trouve également que sudo et pfexec sont de bons utilitaires, mais nul besoin d’être aussi enthousiastes.
Apprenants
$_fonctionne dans plus de coques que bash et c'est une variable appropriée, contrairement à !$. (et @LeartS: J'ai bien aimé le manque d'émoticônes de votre blague.;))
ΩΤΖΙΟΥ
7

Il suffit de mettre un espace entre! et "que ça va marcher.

À votre santé.

Isik Mater
la source
2
Pourriez-vous s'il vous plaît ajouter une explication quant à pourquoi votre solution fonctionne?
200_success
2
Cela fonctionne parce que Bash ne traite que! en tant que caractère spécial s'il est directement suivi d'un caractère non blanc. Cependant, cela ajoutera également un blanc supplémentaire à votre sortie, de sorte que cela ne soit pas toujours souhaitable ...
mmey
2

Oui, ! est un caractère spécial à bash, il est utilisé pour faire référence aux commandes précédentes.

Peu de façons de gérer la situation

Ce qui suit va afficher la chaîne telle quelle.

echo 'Reboot your instance!'

Ce qui suit va exécuter la commande et concaténer la chaîne

echo 'escaping #'" adding `which python` to the string"
echo '#!'`which python`
Syed Qaiser
la source
1

Il bash 4.3semble qu'il ne soit plus nécessaire d'utiliser des guillemets simples ou set +H:

$ bash --version
GNU bash, version 4.3.46(1)-release (x86_64-unknown-linux-gnu)
[...]
$ echo "Reboot your instance!"
Reboot your instance!
Eugene Yarmash
la source
Probablement un bug, car ici, j'ai 4.4.12 et encore une fois, j'ai besoin de set +Hciter. Ou vous avez set +Hquelque part (comme dans /etc/profile).
Bodo Thiesen
Utiliser! -Char pas à la fin de la phrase vérifie que la détection d'événements de bash fonctionne toujours bien. La version est 4.3.30 ........ $ echo "Revérifiez votre phrase! Foo" bash
:!
0

Vous pouvez aussi utiliser un heredoc :

cat <<EOF
Reboot your instance!
EOF

C'est agaçant dans ce cas parce que ce n'est pas un one-liner mais pour des commandes plus longues (comme écrire un script bash), cela fonctionne très bien.

Tom Saleeba
la source