Vous ne pouvez pas utiliser le point d'exclamation (!) Dans bash?

87

J'essaie d'utiliser la commande curl pour accéder à une URL http avec un point d'exclamation ( !) dans son chemin. par exemple:

curl -v "http://example.org/!287s87asdjh2/somepath/someresource"

la console répond avec bash: ... event not found.

Qu'est-ce qui se passe ici? et quelle serait la syntaxe appropriée pour échapper au point d'exclamation?

netbrain
la source
Résolu à bash 4.4+
Isaac

Réponses:

99

Le point d'exclamation fait partie de l'expansion de l'historique dans bash. Pour l'utiliser, vous devez le mettre entre guillemets simples (par exemple:) 'http://example.org/!132'ou l'échapper directement avec une barre oblique inverse ( \) avant le caractère (par exemple:) "http://example.org/\!132".

Notez que, entre guillemets, une barre oblique inverse avant le point exclut empêche l’extension de l’historique, MAIS la barre oblique inverse n’est pas supprimée dans un tel cas. Il est donc préférable d’utiliser des guillemets simples afin de ne pas transmettre une barre oblique inverse littérale à curll’URL.

Daniel Pittman
la source
8
"http://example.org/\!132"s’agrandit sans interpréter la barre oblique inversée (pour des raisons de conformité POSIX, je crois).
Chris Down
@ChrisDown, j'ai essayé de préciser que c'était ma deuxième option dans le texte. Merci de signaler le risque de confusion.
Daniel Pittman
6
Pour mémoire: Ce n'est pas portable d'essayer de s'échapper "!". La recommandation de meilleures pratiques est de toujours citer (guillemets) "!". En relation: "^" (caret), est un non métacaractère nécessitant une citation pour la portabilité. Finalement, "!" ne devrait pas être utilisé dans une instruction if; utilisez-le comme argument pour tester si possible (à nouveau à cause de Solaris / bin / sh).
Nicholas Wilson
5
Seules les citations simples ont fonctionné pour moi. zsh était toujours en train d'interpréter \!et de guillemets doubles.
Orkoden
1
Sous Solaris (ancien shell antérieur à XPG4), '^' est un alias pour |et est utilisé pour créer un tuyau. Si vous envoyez des scripts aux clients et que vous ne savez pas dans quel shell ils vont l'exécuter, vous devez tous les tester!
Nicholas Wilson
61

En plus de la réponse donnée par Daniel, vous pouvez également simplement désactiver l'extension de l'historique si vous ne l'utilisez pas set +H.

Chris Down
la source
19
Désactiver complètement l’expansion de l’histoire est le meilleur conseil que j’ai entendu toute la journée! L'expansion de l'historique est dangereuse et byzantine lorsqu'il existe de bien meilleures alternatives (recherche d'historique incrémentielle avec Ctrl-R) qui vous permettent de prévisualiser et d'éditer votre commande afin que vous ne tiriez pas à l'aveuglette avec une commande !-14que vous aviez à faire !-12, oups, se trouvait être rm -rf *. Fais attention. Désactiver l'expansion de l'historique! Esquive le !!
Aculich
6
La plus grande réponse: l'expansion de l'historique est un risque de sécurité énorme! Il peut être utilisé pour attaquer votre Unix via une URL spécialement construite.
dan
@aculich, ou utilisez simplement la commande spécifiée par POSIX fc -14. Mais il est vrai que vous pouvez le faire sans que l'extension de l'historique ne soit également activée. Personnellement, j'utilise !$et !viet sudo !!et même git add !vi:$assez souvent pour justifier de laisser l'expansion de l'historique activée.
Wildcard
Je pense que je vais ajouter ceci à mes fichiers shell RC. Je n’ai jamais utilisé cela comme un "truc"
génial
17

Personnellement, je ferais des guillemets simples, mais par souci d'exhaustivité, je noterai aussi qu'il s'agit d'une URL, vous pouvez coder le !comme %21, par exemple curl -v http://example.org/%21132.

Aaron D. Marasco
la source
13

Cela peut aussi faire

curl -v "http://example.org/"'!'"287s87asdjh2/somepath/someresource"
ou
curl -v "http://example.org/"\!"287s87asdjh2/somepath/someresource"

Ce qui fonctionne car bash concatène les chaînes adjacentes. Cette approche est particulièrement utile lorsque d'autres éléments nécessitent une expansion du shell. Vous ne pouvez donc pas utiliser de guillemets simples pour toute la chaîne:

curl -v 'http://example.org/!'"287s87asdjh2/${basepath}/someresource"

!Le caractère est utilisé pour les développements de l'historique dans l'invite de ligne de commande.
cela peut donc poser un problème dans prompt mais pas dans les fichiers de script shell.
comme vous pouvez le voir, les extensions d’historique fonctionnent même entre guillemets doubles.

mug896
la source
Il y a beaucoup de façons de créer des commandes Unix et les phrases anglaises utilisent plus de caractères qu'elles n'en ont besoin et sont plus déroutantes qu'elles ne le devraient. Comment est-ce supérieur à la première réponse / accepté / le plus voté, à savoir, mettre l'URL entière entre guillemets?
G-Man
2
@ G-Man: Il indique une autre façon de construire des arguments bash. Je n'étais pas au courant de cette méthode. Rien de mal à apprendre de nouvelles choses.
Sahil Singh
@SahilSingh Comment est-ce nouveau? Il concatène trois chaînes, deux entre guillemets doubles et une entre guillemets simples. Il n'y a pas de nidification ici.
Raphaël
@ G-Man Il n'est pas évident que lorsque vous mettez deux chaînes l'une à côté de l'autre, elles soient concaténées. printf ("hello" "world") fonctionnerait également en c, mais printf ("hello" 'w') ne fonctionnerait pas, vous voyez donc que savoir que bash accepte de telles expressions était nouveau pour moi, mais je suis d'accord avec point de vue de l'utilité, ce n'est pas supérieur. J'ai aimé la réponse, tout comme Mark Shust.
Sahil Singh
2
@ G-Man Il est également utile quand il y a d' autres extensions de chaîne on ne veux arriver dans la même chaîne. C'est un moyen facile de séparer deux types de comportement de citation.
WAF
7

J'ai rencontré le même problème et ma solution simple consistait à utiliser une variable:

E=!  
curl -v "http://example.org/${E}287s87asdjh2/somepath/someresource"

Ici, la simplicité est que (1) Il est portable entre les commandes et les commandes (2) Ne nécessite pas de connaître la syntaxe d'échappement et les codes ASCII.

Prem
la source
3

Depuis Bash 4.3, vous pouvez maintenant utiliser des guillemets doubles pour citer le caractère d'expansion de l'historique:

$ bash --version
GNU bash, version 4.3...
[...]
$ echo "Hello World!"
Hello World!
Flimm
la source
cela ne fonctionne pas en dehors de echo, écho semble traiter cela différemment
phil294
@Blauhirn Cela n'a rien à voir avec echo, et tout à voir avec les citations et la version de bash que vous utilisez.
Flimm
2
Cette réponse est fausse et devrait être supprimée. Votre bashversion n'a rien à voir avec le bang non développé, c'est dû au fait que dans votre exemple, le !mot "fin de ligne" est suivi et que cela empêche le shell d'essayer de le développer. Essayez echo "!Hello World"et vous verrez qui bashrépondra avec bash: !Hello: event not found. Voir le manuel pour plus de détails
don_crissti
0

Pour ceux qui utilisent git bash dans Windows, la réponse acceptée de @DanielPittman fonctionne. Cependant, vous devez remplacer la barre oblique inverse (\) par une barre oblique (/).

Par exemple, sous Unix, cela ressemblerait à ceci:

curl https://abc.com/services -H 'Authorization: Bearer 111A80BBZZCnS\!ZR412543s'

Pour Windows, ce serait quelque chose comme ceci (concentrez-vous sur la barre oblique dans la partie en-tête d'autorisation)

curl https://abc.com/services -H 'Authorization: Bearer 111A80BBZZCnS/!ZR412543s'

SamuelDev
la source
Cela n'a pas beaucoup de sens. L'argument single est cité, donc, quelles que soient les barres obliques impliquées, l'exclam n'entraînera pas d'expansion de l'historique.
Wildcard
Ohh tu as raison. Je n'ai posté cette réponse que parce que lorsque j'utilisais la réponse de Daniel (en utilisant une barre oblique inversée), une erreur s'est produite.
SamuelDev