Kann in bash kein Ausrufezeichen (!) Verwendet werden?

86

Ich versuche, mit dem Befehl curl auf eine http-URL mit einem Ausrufezeichen ( !) im Pfad zuzugreifen . z.B:

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

Die Konsole antwortet mit bash: ... event not found.

Was geht hier vor sich? und was wäre die richtige Syntax, um dem Ausrufezeichen zu entgehen?

netbrain
quelle
In Bash 4.4+ gelöst
Isaac

Antworten:

98

Das Ausrufezeichen ist Teil der Verlaufserweiterung in Bash. Um es zu benutzen, müssen Sie es in einfache Anführungszeichen setzen (zB:) 'http://example.org/!132'oder es direkt mit einem Backslash ( \) vor dem Zeichen (zB:) schließen "http://example.org/\!132".

Beachten Sie, dass in doppelten Anführungszeichen ein Backslash vor dem Exklam die Verlaufserweiterung verhindert, der Backslash jedoch in einem solchen Fall nicht entfernt wird. Es ist daher besser, einfache Anführungszeichen zu verwenden, damit Sie keinen wörtlichen Backslash curlals Teil der URL übergeben.

Daniel Pittman
quelle
8
"http://example.org/\!132"Erweitert sich tatsächlich, ohne den Backslash zu interpretieren (aus Gründen der POSIX-Konformität, glaube ich).
Chris Down
@ ChrisDown, ich habe versucht zu verdeutlichen, dass dies meine zweite Option im Text war. Vielen Dank, dass Sie auf die mögliche Verwirrung hingewiesen haben.
Daniel Pittman
6
Für die Aufzeichnung: Es ist nicht übertragbar, zu versuchen, "!" Zu entkommen. Die Best-Practices-Empfehlung lautet, immer (einfache Anführungszeichen) "!" Verwandt: "^" (Caret) ist ein Nicht-Metazeichen, das aus Gründen der Portabilität in Anführungszeichen gesetzt werden muss. Schließlich, "!" sollte nicht in einer if-Anweisung verwendet werden; Verwenden Sie es stattdessen als Argument, um zu testen, falls dies möglich ist (erneut aufgrund von Solaris / bin / sh).
Nicholas Wilson
5
Bei mir haben nur einfache Anführungszeichen funktioniert. zsh interpretierte immer noch \!und doppelte Anführungszeichen.
Orkoden
1
Unter Solaris (alte Pre-XPG4-Shell) ist '^' ein Alias ​​für |und wird zum Erstellen einer Pipe verwendet. Wenn Sie Skripte an Kunden senden und nicht sicher sind, in welcher Shell sie sie ausführen, müssen Sie sie alle testen!
Nicholas Wilson
61

Neben der Antwort von Daniel können Sie die Verlaufserweiterung auch ganz einfach deaktivieren, wenn Sie sie nicht verwenden set +H.

Chris Down
quelle
19
Das Ausschalten der Verlaufserweiterung ist der beste Rat, den ich den ganzen Tag gehört habe! Verlaufserweiterung ist gefährlich und byzantinisch, wenn es viel bessere Alternativen gibt (inkrementelle Verlaufssuche mit Ctrl-R), mit denen Sie Ihren Befehl in der Vorschau anzeigen und bearbeiten können, damit Sie nicht blindlings mit dem Befehl abfeuern !-14, den Sie !-12gerade ausgeführt haben rm -rf *. Sicher sein. Verlaufserweiterung deaktivieren! Eschew the !!
ACULICH
6
Größte Antwort: Die Erweiterung der Historie ist ein großes Sicherheitsrisiko! Es kann verwendet werden, um Ihr Unix über eine gestaltete URL anzugreifen.
dan
@aculich, oder verwenden Sie stattdessen einfach den von POSIX angegebenen Befehl fc -14. Aber es ist wahr, dass Sie dies tun können, ohne dass die Verlaufserweiterung ebenfalls aktiviert ist. Persönlich verwende ich !$und !viund sudo !!und sogar git add !vi:$oft genug, um zu gewährleisten, dass die Historienerweiterung aktiviert bleibt.
Wildcard
Ich denke, ich werde dies zu meinen Shell-RC-Dateien hinzufügen. Ich habe das immer nur als "Trick" benutzt
TonyH
17

Ich persönlich würde tun einfache Anführungszeichen, aber der Vollständigkeit halber werde ich auch zur Kenntnis , da es sich um eine URL ist, können Sie die kodieren , können !wie %21zB curl -v http://example.org/%21132.

Aaron D. Marasco
quelle
13

Das kann man auch

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

Das funktioniert, weil Bash benachbarte Zeichenfolgen verkettet. Dieser Ansatz ist besonders nützlich, wenn Sie andere Dinge haben, die eine Shell-Erweiterung erfordern, sodass Sie keine einfachen Anführungszeichen für die gesamte Zeichenfolge verwenden können:

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

!Das Zeichen wird für Verlaufserweiterungen in der Befehlszeile verwendet.
Daher kann dies ein Problem in Eingabeaufforderungen, jedoch nicht in Shell-Skriptdateien sein.
Wie Sie sehen können, funktionieren Verlaufserweiterungen sogar in doppelten Anführungszeichen.

mug896
quelle
Es gibt viele Möglichkeiten, Unix-Befehle und englische Sätze so zu gestalten, dass sie mehr Zeichen als nötig enthalten und verwirrender sind als nötig. Wie ist dies der ersten / akzeptierten / am höchsten bewerteten Antwort überlegen, nämlich die gesamte URL in einfache Anführungszeichen zu setzen?
G-Man,
2
@ G-Man: Es gibt eine andere Möglichkeit, bash-Argumente zu konstruieren. Diese Methode war mir nicht bekannt. Es ist nichts falsch daran, neue Dinge zu lernen.
Sahil Singh
@SahilSingh Wie ist das neu? Es verkettet drei Zeichenfolgen, zwei in doppelten Anführungszeichen und eine in einfachen Anführungszeichen. Hier gibt es keine Verschachtelung.
Raphael
@ G-Man Es ist nicht offensichtlich, dass wenn Sie 2 Zeichenfolgen nebeneinander platzieren, diese verkettet werden. printf ("hello" "world") würde auch in c funktionieren, aber printf ("hello" 'w') wird nicht funktionieren, daher war es für mich neu zu wissen, dass bash solche Ausdrücke enthält, aber ich stimme zu Utility-Sicht ist dies nicht überlegen. Die Antwort gefiel mir, ebenso wie Mark Shust.
Sahil Singh
2
@ G-Man Es ist auch nützlich, wenn es andere Zeichenfolgenerweiterungen gibt , die in derselben Zeichenfolge auftreten sollen. Dies ist eine einfache Möglichkeit, zwei Arten von Zitierverhalten zu trennen.
WAF
7

Ich bin auf dasselbe Problem gestoßen, und meine einfache Lösung bestand darin, eine Variable zu verwenden:

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

Hier ist die Einfachheit, dass (1) Es ist portabel über Shells und Befehle (2) Erfordert keine Kenntnisse der Escape-Syntax und ASCII-Codes.

Prem
quelle
3

Seit Bash 4.3 können Sie doppelte Anführungszeichen verwenden, um das Verlaufserweiterungszeichen zu zitieren:

$ bash --version
GNU bash, version 4.3...
[...]
$ echo "Hello World!"
Hello World!
Flimm
quelle
Dies funktioniert nicht außerhalb von echo, Echo scheint dies anders zu behandeln
Phil294
@Blauhirn Das hat nichts mit Echo zu tun, und alles was mit Zitieren und der Version von Bash zu tun hat, die Sie ausführen.
Flimm
2
Diese Antwort ist falsch und sollte gelöscht werden. Ihre bashVersion hat nichts damit zu tun, dass der Knall nicht erweitert wird. Dies liegt daran, dass in Ihrem Beispiel !hinter dem "Zeilenende" ein "Zeilenende" steht und die Shell nicht versucht, ihn zu erweitern. Versuchen echo "!Hello World"Sie es und Sie werden sehen, dass Sie bashmit antworten bash: !Hello: event not found. Siehe Handbuch für weitere Details
don_crissti
0

Für diejenigen, die git bash in Windows verwenden, funktioniert die akzeptierte Antwort von @DanielPittman. Sie sollten jedoch den umgekehrten Schrägstrich (\) durch einen Schrägstrich (/) ersetzen.

In Unix sieht es beispielsweise so aus:

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

Für Windows wäre es ungefähr so ​​(Fokus auf den Schrägstrich im Autorisierungsheader)

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

SamuelDev
quelle
Das ergibt wenig Sinn. Sie haben das Argument in Anführungszeichen gesetzt, sodass das Ausrufezeichen unabhängig von den Schrägstrichen nicht zu einer Erweiterung des Verlaufs führt.
Wildcard
Ohh du hast recht Ich habe diese Antwort nur gepostet, weil bei der Verwendung von Daniels Antwort (Backslash) ein Fehler aufgetreten ist.
SamuelDev