Ich möchte fragen:
Warum wird echo {1,2,3}
auf 1 2 3 erweitert, was ein erwartetes Verhalten ist, während echo [[:digit:]]
zurückgegeben wird, [[:digit:]]
während ich erwartet habe, dass alle Ziffern von 0
bis gedruckt werden 9
?
Ich möchte fragen:
Warum wird echo {1,2,3}
auf 1 2 3 erweitert, was ein erwartetes Verhalten ist, während echo [[:digit:]]
zurückgegeben wird, [[:digit:]]
während ich erwartet habe, dass alle Ziffern von 0
bis gedruckt werden 9
?
Antworten:
Weil es zwei verschiedene Dinge sind. Dies {1,2,3}
ist ein Beispiel für die Erweiterung der Zahnspange . Das {1,2,3}
Konstrukt wird von der Shell erweitert , bevor es echo
überhaupt angezeigt wird. Sie können sehen, was passiert, wenn Sie Folgendes verwenden set -x
:
$ set -x
$ echo {1,2,3}
+ echo 1 2 3
1 2 3
Wie Sie sehen können, wird der Befehl echo {1,2,3}
erweitert auf:
echo 1 2 3
Ist [[:digit:]]
jedoch eine POSIX-Zeichenklasse . Wenn Sie es geben echo
, verarbeitet die Shell es auch zuerst, aber dieses Mal wird es als Shell-Glob verarbeitet . Es funktioniert genauso, als ob Sie ausführen, echo *
wodurch alle Dateien im aktuellen Verzeichnis gedruckt werden. [[:digit:]]
Ist aber ein Shell Glob, der mit jeder Ziffer übereinstimmt. Wenn ein Shell-Glob in Bash mit nichts übereinstimmt, wird er auf sich selbst erweitert:
$ echo /this*matches*no*files
+ echo '/this*matches*no*files'
/this*matches*no*files
Wenn der Globus mit etwas übereinstimmt, wird dieser gedruckt:
$ echo /e*c
+ echo /etc
/etc
In beiden Fällen wird echo
nur gedruckt, was die Shell zum Drucken auffordert. Im zweiten Fall wird jedoch angegeben, dass der Globus gedruckt werden soll, da der Glob mit etwas übereinstimmt ( /etc
).
Da Sie also keine Dateien oder Verzeichnisse haben, deren Name aus genau einer Ziffer besteht (was [[:digit:]]
übereinstimmen würde), wird der Glob auf sich selbst erweitert und Sie erhalten:
$ echo [[:digit:]]
[[:digit:]]
Versuchen Sie nun, eine Datei mit dem Namen zu erstellen 5
und denselben Befehl auszuführen:
$ echo [[:digit:]]
5
Und wenn es mehr als eine übereinstimmende Datei gibt:
$ touch 1 5
$ echo [[:digit:]]
1 5
Dies ist (irgendwie) man bash
in der Erläuterung der nullglob
Optionen dokumentiert , die dieses Verhalten ausschalten:
nullglob
If set, bash allows patterns which match no files (see
Pathname Expansion above) to expand to a null string,
rather than themselves.
Wenn Sie diese Option einstellen:
$ rm 1 5
$ shopt -s nullglob
$ echo [[:digit:]] ## prints nothing
$
shopt -s failglob
, um ein nützlicheres Verhalten zu erhalten, das dem von modernen Muscheln wie zsh
oder ähnelt fish
.
failglob
. nullglob
kann zu unerwarteten Problemen führen, z. B. beim Einfügen einer URL mit einer ?
.
nullglob
, um zu demonstrieren, dass das Muster von der Shell als Glob interpretiert wird.
{1,2,3}
ist Klammernerweiterung , dehnt es sich auf die Worte , ohne Rücksicht aufgeführt ihre Bedeutung.
[...]
ist eine Zeichengruppe, die in der Dateinamenerweiterung (oder im Platzhalter oder Glob) ähnlich wie das Sternchen *
und das Fragezeichen verwendet wird ?
. Es entspricht jedem einzelnen darin aufgelisteten Zeichen oder Zeichen, die Mitglieder benannter Gruppen sind, z. B. [:digit:]
wenn diese aufgelistet sind. Das Standardverhalten der meisten Shells besteht darin, den Platzhalter unverändert zu lassen, wenn keine passenden Dateien vorhanden sind.
(Beachten Sie, dass Sie einen Platzhalter / ein Muster nicht wirklich in eine Reihe von Zeichenfolgen umwandeln können, mit denen es übereinstimmen würde. Das Sternchen kann mit einer beliebigen Zeichenfolge beliebiger Länge übereinstimmen. Wenn Sie also ein beliebiges Muster erweitern, das es enthält, wird eine unendliche Liste von Zeichenfolgen erstellt.)
So:
$ bash -c 'echo [[:digit:]]' # bash leaves it as-is
[[:digit:]]
$ zsh -c 'echo [[:digit:]]' # zsh by default complains if no match
zsh:1: no matches found: [[:digit:]]
$ touch 1 3 d i g t
$ bash -c 'echo [[:digit:]]' # now there are two matches
1 3 # note that d, i, g and t do NOT match
Aber dennoch:
$ bash -c 'echo {1,2,3}'
1 2 3
Beide werden durch die Shell erweitert . Es spielt keine Rolle, ob der von Ihnen ausgeführte Befehl ls
oder echo
oder ist rm
. Beachten Sie auch, dass wenn eines dieser Elemente zitiert wird, es nicht erweitert wird:
$ bash -c 'echo "[[:digit:]]"' # even though matching files still exist
[[:digit:]]
$ bash -c 'echo "{1,2,3}"'
{1,2,3}
[[:digit:]]
vor dem Weitergeben an echo
, echo
sieht also nie [[:digit:]]
, sie sieht nur 1 3
. Sie können dies in Aktion sehen, indem Sie set -x
ausführen, wodurch die tatsächlich ausgeführten Befehle gedruckt werden (ausführen set +x
, um sie wieder auszuschalten).
echo
sucht nicht nach Dateien, die Shell tut dies, bevor das ausgeführt wird echo
.
{1,2,3}
(und zB {1..3}
sind Verstrebung Expansionen . Sie werden von der Schale , bevor die Befehlsausführung interpretiert.
[[:digit:]]
ist ein Mustervergleichstoken , aber Sie verwenden es nicht an einem Speicherort mit Dateien, die diesem Muster entsprechen. Wenn Sie eine Musterübereinstimmung verwenden, die keine Übereinstimmungen aufweist, wird sie auf sich selbst erweitert:
$ echo [[:digit:]]; touch 3; echo [[:digit:]]
[[:digit:]]
3