web-dev-qa-db-fra.com

Comment écho à un bang!

J'ai essayé de créer un script par echo 'ing dans un fichier, au lieu de l'ouvrir avec un éditeur

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

La sortie :

bash:!/bin/bash: événement non trouvé

J'ai isolé ce comportement étrange au Bang .

$ echo !
!  

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

$ echo \#!
#!

$ echo \#!/bin/bash
bash: !/bin/bash: event not found
  • Pourquoi Bang provoque-t-il cela?
  • Quels sont ces "événements" que Bash se réfère?
  • Comment puis-je passer ce problème et imprimer "#!/Bin/bash" à l'écran ou à mon fichier?
59
Stefan

Essayez d'utiliser des guillemets simples.

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

echo '#!'

echo '#!/bin/bash'

Le problème se produit car Bash recherche son histoire pour!/Bin/bash. L'utilisation de citations simples échappe à ce comportement.

62
Richm

AS Richm a déclaré , bash tente de faire une Historique correspondance . Une autre façon d'éviter c'est de s'échapper avec un \:

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

Bien que méfiez-vous que dans les citations doubles, le \ n'est pas supprimé:

$ echo "\!"
\!
18
Michael Mrozek

! Démarre une substitution d'historique (un "événement" est une ligne dans l'historique de la commande); par exemple !ls se développe à la dernière ligne de commande contenant ls et !?foo Développe à la dernière ligne de commande contenant foo. Vous pouvez également extraire des mots spécifiques (par ex. !!:1 fait référence au premier mot de la commande précédente) et plus encore; Voir le manuel pour plus de détails.

Cette fonctionnalité a été inventée pour rappeler rapidement les commandes précédentes dans les jours où l'édition de ligne de commande était primitive. Avec des coquilles modernes (au moins Bash et ZSH) et la copie-coller, l'expansion de l'historique n'est pas aussi utile que celle-ci, elle est toujours utile, mais vous pouvez vous en tirer sans elle.

Vous pouvez modifier le caractère qui déclenche le caractère de substitution de l'historique en définissant la variable histchars; Si vous utilisez rarement la substitution d'historique, vous pouvez définir par exemple. histchars='¡^' pour que ¡ déclenche l'expansion de l'histoire au lieu de !. Vous pouvez même éteindre complètement la fonction avec set +o histexpand.

Pour pouvoir désactiver l'expansion de l'historique sur une ligne de commande particulière, vous pouvez utiliser space comme le 3ème caractère de $histchars:

histchars='!^ '

Ensuite, si vous entrez votre commande avec un espace de premier plan, l'expansion de l'historique ne sera pas effectuée.

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

REMARQUE Toutefois que des espaces principaux sont également utilisés lorsque $HISTCONTROL contient ignorespace comme moyen de dire bash non d'enregistrer une ligne de commande dans l'historique.

Si vous souhaitez que les deux fonctionnalités soient indénicatives, vous devez choisir un autre caractère comme le 3ème caractère de $histchars. Vous voulez un qui n'affecte pas la façon dont votre commande est interprétée. Quelques options:

  • utilisation de barreaux Backslash (\): \echo foo fonctionne mais qui a l'effet secondaire de désactiver des alias ou des mots-clés.
  • Onglet: Pour l'insérer en première position, vous devez appuyer sur Ctrl+VTab bien que.
  • si cela ne vous dérange pas de taper deux touches, vous pouvez choisir n'importe quel caractère qui n'apparaît normalement pas en première position (%, @, ?, choisissez vous-même) et faire un alias vide pour cela:

    histchars='!^%'
    alias %=
    

    Ensuite, entrez ce caractère, espace et votre commande:

    bash-4.3$ % echo !!
    !!
    

(Vous ne pourrez pas ne pas enregistrer une commande où la substitution d'historique a été désactivée. Notez également que le 3ème caractère par défaut de $histchars est # de sorte que l'expansion de l'historique n'est pas terminée. Dans les commentaires. Si vous le changez et que vous entrez des commentaires à l'invite, vous devez être conscient du fait que! Les séquences peuvent être étendues là-bas).

4
Stéphane Chazelas

Les solutions proposées ne fonctionnent pas dans, par exemple, l'exemple suivant:

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

Dans ce cas, le Bang peut être imprimé à l'aide de son octal ASCII code:

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

Une solution simple non encore mentionnée est d'utiliser une variable:

$ var='!'
$ echo -e "#${var}/bin/bash \n /usr/bin/command args"  > .scripts/command

Impression d'un bang! est non Un problème lorsque la chaîne est incluse ou est unique citée ou à l'intérieur d'une chaîne C

$ echo hi\!pal 'hi!pal' $'hi!pal'
hi!pal 'hi!pal' $'hi!pal'

Il n'est pas non plus un problème lorsque Histexpand est désactivé (soit shopt -ou histexpand ou set +o histexpand ou simplement set +H. Il est également possible de changer les caractères hissexpand pour faciliter l'impression d'un bang!.

Mais la solution la plus simple portable est: utilisez un var

$ var='!'
$ echo "hi${var}pal"
hi!pal
0
Isaac