Unerwartetes Verhalten mit Echo [[: Ziffer:]]

10

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 0bis gedruckt werden 9?

AbdAllah Talaat
quelle
Achten Sie auf: unix.stackexchange.com/q/347950/117549
Jeff Schaller

Antworten:

34

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 echonur 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 5und 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 bashin der Erläuterung der nullglobOptionen 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

$ 
terdon
quelle
4
Siehe auch shopt -s failglob, um ein nützlicheres Verhalten zu erhalten, das dem von modernen Muscheln wie zshoder ähnelt fish.
Stéphane Chazelas
Ich stimme Stéphane zu failglob. nullglobkann zu unerwarteten Problemen führen, z. B. beim Einfügen einer URL mit einer ?.
Kevin
1
Klar, ich habe nur erwähnt nullglob, um zu demonstrieren, dass das Muster von der Shell als Glob interpretiert wird.
Terdon
14

{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 lsoder echooder 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}
ilkkachu
quelle
danke für deine antwort, bin neu in linux, also lass mich dich bitte fragen, wie echo mit dateien 1 3 zusammenhängt. es ist die funktion, seine argumente zu drucken, um nicht nach
dateien zu
1
@AbdAllahTalaat das hat eigentlich nichts mit echo zu tun. Die Shell (z. B. Bash) "erweitert" sich [[:digit:]] vor dem Weitergeben an echo, echosieht also nie [[:digit:]], sie sieht nur 1 3. Sie können dies in Aktion sehen, indem Sie set -xausführen, wodurch die tatsächlich ausgeführten Befehle gedruckt werden (ausführen set +x, um sie wieder auszuschalten).
Terdon
@AbdAllahTalaat, echosucht nicht nach Dateien, die Shell tut dies, bevor das ausgeführt wird echo.
Ilkkachu
Zumal ich denke, dass unter DOS / Windows die Dienstprogramme die Platzhalter erweitern, nicht die Shell. (Ich kann mich irren)
ilkkachu
Tut mir leid, Leute, ich habe die richtige Antwort auf Tedrons Antwort verschoben, weil sein Kommentar die Bedeutung enthielt, dass Bash das ist, was die Arbeit nicht wiedergibt. Auch seine Antwort enthielt diese Bedeutung. Sie alle haben mir geholfen. Ich wünschte, ich könnte es sagen richtige Antwort für alle Ihre Antworten und Kommentare
AbdAllah Talaat
4

{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
DopeGhoti
quelle
Nein, wie die anderen Antworten richtig anzeigen, wird das Muster mit Dateinamen abgeglichen.
Toby Speight