Zsh behandelt einen String als Dateinamenmuster und beschwert sich darüber (Option NOMATCH)

Zsh behandelt einen String als Dateinamenmuster und beschwert sich darüber (Option NOMATCH)

Ich probiere Zsh aus, das von Bash kommt. Ich hatte ein bisschen Probleme zu verstehen, warum Zsh sich über einen Grep-Regex beschwert.

usr@rk1 ~ % tty | grep ^/dev/tty[1-7]$ > /dev/null 2>&1
zsh: no matches found: ^/dev/tty[1-7]$

Mir ist aufgefallen, dass die Ausgabe verschwindet und alles in Ordnung ist, wenn (1) ich den regulären Ausdruck in Anführungszeichen einschließe, (2) die Schrägstriche aus dem regulären Ausdruck entferne oder (3) verwende setopt NO_NOMATCH.

Aus dem zsoptions-Handbuch:

NOMATCH (+3) <C> <Z>

Wenn ein Muster zur Generierung von Dateinamen keine Übereinstimmungen aufweist, drucken Sie einen Fehler aus, anstatt es unverändert in der Argumentliste zu belassen. Dies gilt auch für die Dateierweiterung eines anfänglichen '~' oder '='.

Mir scheint also, dass der reguläre Ausdruck aufgrund der Schrägstriche als Dateinamenmuster behandelt wird. Ist das normal oder ein Fehler?

Außerdem wäre es toll, etwas Ähnliches wie die Bashism- und BashPitfalls-Seiten von GreyCat zu haben, aber für Zsh. Kennen Sie eine solche Ressource?

Antwort1

Wenn Sie die EXTENDED_GLOBOption aktiviert haben, wird das Muster ^somethingfür die Generierung von Dateinamen verwendet.zshexpnManpage:

^x (Erfordert die Festlegung von EXTENDED_GLOB.) Stimmt mit allem überein, außer mit dem Muster x. Dies hat eine höhere Priorität als /, daher ^foo/barwerden alle Verzeichnisse in durchsucht, .außer ./foonach einer Datei mit dem Namen bar.

Es wird also ^/dev/tty[1-7]$versucht, in jedem Unterverzeichnis (nichts wird ausgeschlossen, da ^direkt von gefolgt wird /) des aktuellen Verzeichnisses nach einer Datei mit dem Namen dev/tty1$, dev/tty2$, ... zu suchen, dev/tty7$die [1-7]genau einem Zeichen im Bereich zwischen 1 und 7 entspricht. $wird in diesem Fall nicht speziell behandelt.

Sie haben die Lösung bereits gefunden: Setzen Sie das Caret NO_NOMATCH-Zeichen (könnte gefährlich sein, gefällt mir aber sehr gut, da ich zu faul zum Tippen bin ;)) oder setzen Sie es in Anführungszeichen (einfache oder doppelte Anführungszeichen).

Versuchen Sie es mit printdem, was passiert:

$ setopt nomatch extended_glob
$ echo ^/dev/tty[1-7]$
zsh: no matches found: ^/dev/tty[1-7]$

# ^ is doing filename generation in every sub-dir
$ touch foo/dev/tty5$
$ echo ^/dev/tty[1-7]$
foo/dev/tty5$

# quote to prevent filename generation
$ echo "^/dev/tty[1-7]$"
^/dev/tty[1-7]$
$ echo '^/dev/tty[1-7]$'
^/dev/tty[1-7]$

Ohne EXTENDED_GLOBset wird das Caretzeichen wörtlich als normales Zeichen interpretiert. Also sind Anführungszeichen wieder Ihr Freund -- dieses Mal [1-7]löst es nur die Generierung des Dateinamens aus:

$ setopt nomatch no_extended_glob
$ echo ^/dev/tty[1-7]$
zsh: no matches found: ^/dev/tty[1-7]$

# quote to prevent filename generation
$ echo ^/dev/tty"[1-7]"$
^/dev/tty[1-7]$

# caret is interpreted literally
$ mkdir ^/dev -p
$ touch ^/dev/tty5$
$ echo ^/dev/tty[1-7]$
^/dev/tty5$

Zusammenfassend lässt sich also sagen: Das Zitieren von Regex-Mustern scheint mir immer eine gute Idee zu sein – obwohl ich normalerweise auch versuche, einige Tastenanschläge zu sparen.

verwandte Informationen