Wie entkomme ich dem Platzhalter- / Sternchenzeichen in Bash?


136

Beispielsweise:

me$ FOO="BAR * BAR"
me$ echo $FOO
BAR file1 file2 file3 file4 BAR

und mit dem \Escape-Zeichen:

me$ FOO="BAR \* BAR"
me$ echo $FOO
BAR \* BAR

Ich mache offensichtlich etwas Dummes.

Wie bekomme ich die Ausgabe BAR * BAR?

Antworten:


139

Zitieren, wenn die Einstellung $FOOnicht ausreicht. Sie müssen auch die Variablenreferenz angeben:

me$ FOO="BAR * BAR"
me$ echo "$FOO"
BAR * BAR

8
das ist mysteriös, warum ist das so? Was ist los?
Tofutim

Weil sich die Variablen erweitern
Daniel

103

KURZE ANTWORT

Wie andere gesagt haben - Sie sollten immer die Variablen zitieren, um seltsames Verhalten zu verhindern. Also benutze echo "$ foo" in anstatt nur echo $ foo .

LANGE ANTWORT

Ich denke, dieses Beispiel verdient weitere Erklärung, da mehr los ist, als es auf den ersten Blick erscheinen mag.

Ich kann sehen, wo Ihre Verwirrung ins Spiel kommt, denn nachdem Sie Ihr erstes Beispiel ausgeführt haben, haben Sie sich wahrscheinlich gedacht, dass die Shell offensichtlich Folgendes tut:

  1. Parametererweiterung
  2. Dateinamenerweiterung

Also von Ihrem ersten Beispiel:

me$ FOO="BAR * BAR"
me$ echo $FOO

Nach Parametererweiterung entspricht:

me$ echo BAR * BAR

Und nach Dateinamenerweiterung ist gleichbedeutend mit:

me$ echo BAR file1 file2 file3 file4 BAR

Und wenn Sie nur tippen echo BAR * BAR in die Befehlszeile eingeben, werden Sie sehen, dass sie gleichwertig sind.

Sie haben sich also wahrscheinlich gedacht: "Wenn ich dem * entkomme, kann ich die Dateinamenerweiterung verhindern."

Also aus Ihrem zweiten Beispiel:

me$ FOO="BAR \* BAR"
me$ echo $FOO

Nach der Parametererweiterung sollte äquivalent sein zu:

me$ echo BAR \* BAR

Und nach der Dateinamenerweiterung sollte gleichbedeutend sein mit:

me$ echo BAR \* BAR

Und wenn Sie versuchen, "echo BAR \ * BAR" direkt in die Befehlszeile einzugeben, wird tatsächlich "BAR * BAR" ausgegeben, da die Dateinamenerweiterung durch das Escape verhindert wird.

Warum hat die Verwendung von $ foo nicht funktioniert?

Dies liegt daran, dass eine dritte Erweiterung stattfindet - das Entfernen von Angeboten. Aus der Bash-manuellen Angebotsentfernung geht Folgendes hervor:

Nach den vorhergehenden Erweiterungen werden alle nicht zitierten Vorkommen der Zeichen '\', '' 'und' "'entfernt, die nicht aus einer der oben genannten Erweiterungen resultiert haben.

Wenn Sie den Befehl direkt in die Befehlszeile eingeben, ist das Escape-Zeichen nicht das Ergebnis einer vorherigen Erweiterung. BASH entfernt es also, bevor es an den Echo-Befehl gesendet wird. Im zweiten Beispiel war das "\ *" das Ergebnis einer vorherigen Parametererweiterung, daher wird es NICHT entfernt. Infolgedessen erhält das Echo "\ *" und druckt es aus.

Beachten Sie den Unterschied zwischen dem ersten Beispiel - "*" ist nicht in den Zeichen enthalten, die durch Entfernen von Anführungszeichen entfernt werden.

Ich hoffe das macht Sinn. Am Ende die Schlussfolgerung in der gleichen - verwenden Sie einfach Anführungszeichen. Ich dachte nur, ich würde erklären, warum Escape, was logischerweise funktionieren sollte, wenn nur die Erweiterung von Parameter und Dateiname im Spiel ist, nicht funktioniert.

Eine vollständige Erläuterung der BASH-Erweiterungen finden Sie unter:

http://www.gnu.org/software/bash/manual/bashref.html#Shell-Expansions


1
Gute Antwort! Jetzt habe ich nicht das Gefühl, eine so dumme Frage gestellt zu haben. :-)
andyuk

1
Gibt es ein Hilfsprogramm, um den Sonderzeichen zu entkommen?
Jigar Joshi

"Sie sollten immer die Variablen zitieren, um seltsames Verhalten zu verhindern" - wenn Sie sie als Zeichenfolgen verwenden möchten
Angelo


Während die Antwort von @finnw oben die Frage direkt beantwortet, ist diese Antwort viel besser darin, das Warum zu erklären , was viel mehr bei der Extrapolation der Funktionsweise in unseren eigenen Szenarien hilft. Dies ist die Art von Antwort, von der wir mehr sehen müssen.
Nicolas Coombs

54

Ich werde diesem alten Thread etwas hinzufügen.

Normalerweise würden Sie verwenden

$ echo "$FOO"

Ich hatte jedoch auch mit dieser Syntax Probleme. Betrachten Sie das folgende Skript.

#!/bin/bash
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1"

Die *müssen wörtlich weitergegeben werden curl, aber die gleichen Probleme werden auftreten. Das obige Beispiel funktioniert nicht (es wird auf Dateinamen im aktuellen Verzeichnis erweitert) und auch nicht \*. Sie können auch nicht zitieren, $curl_optsda dies als einzelne (ungültige) Option für erkannt wird curl.

curl: option -s --noproxy * -O: is unknown
curl: try 'curl --help' or 'curl --manual' for more information

Daher würde ich die Verwendung der bashVariablen empfehlen $GLOBIGNORE, um die Dateinamenerweiterung insgesamt zu verhindern, wenn sie auf das globale Muster angewendet wird, oder das set -fintegrierte Flag verwenden.

#!/bin/bash
GLOBIGNORE="*"
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1"  ## no filename expansion

Anwendung auf Ihr ursprüngliches Beispiel:

me$ FOO="BAR * BAR"

me$ echo $FOO
BAR file1 file2 file3 file4 BAR

me$ set -f
me$ echo $FOO
BAR * BAR

me$ set +f
me$ GLOBIGNORE=*
me$ echo $FOO
BAR * BAR

4
Tolle Erklärung, danke! Mein Anwendungsfall ist SELECT * FROM etc., dass dies der einzige Weg ist, der funktioniert.
Knutole

1
Vielen Dank für die Präsentation der set-f-Lösung!
Hachre

5
FOO='BAR * BAR'
echo "$FOO"

Dies funktioniert, aber es ist nicht unbedingt erforderlich, die einfachen Anführungszeichen in der ersten Zeile in doppelte Anführungszeichen zu ändern.
Finnw

1
Nein, ist es nicht, aber die Gewohnheit, einfache Anführungszeichen zu verwenden, ist vorzuziehen, wenn Sie spezielle Shell-Zeichen einfügen und keine Ersetzung wünschen.
Zot


3

Es kann sich lohnen, sich daran zu gewöhnen, printfeher als echoauf der Kommandozeile zu verwenden.

In diesem Beispiel bietet es nicht viel Nutzen, kann aber bei komplexeren Ausgaben nützlicher sein.

FOO="BAR * BAR"
printf %s "$FOO"

Warum zum Teufel? printf ist ein separater Prozess (zumindest kein integrierter Bash), und die Verwendung von printf, die Sie demonstrieren, hat keinen Vorteil gegenüber Echo.
Ddaa

2
printf ist eine eingebaute Bash und ich habe in meiner Antwort gesagt: "In diesem Beispiel bietet es nicht viel Nutzen, kann aber bei komplexeren Ausgaben nützlicher sein.
Dave Webb
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.