In den meisten Shells nullglob
ist nicht die Vorgabe. Das bedeutet zum Beispiel, wenn Sie diesen Befehl ausführen
ls *
in einem leeren Verzeichnis wird *
erweitert glob zu einem wörtlichen *
, statt zu einer leeren Liste von Argumenten. Es gibt Möglichkeiten, dieses Verhalten zu ändern, sodass *
in einem leeren Verzeichnis wird eine leere Liste von Argumenten zurückgeben, was intuitiver erscheint.
Gibt es also einen Grund, warum nullglob
ist standardmäßig deaktiviert? Wenn ja, was ist das?
Akzeptierte Antwort:
Der nullglob
Option (was übrigens ein zsh
ist Erfindung, erst Jahre später zu bash
hinzugefügt (2.0
)) wäre in einigen Fällen nicht ideal. Und ls
ist ein gutes Beispiel:
ls *.txt
Oder sein korrekteres Äquivalent:
ls -- *.txt
Mit nullglob
on würde ls
ausführen ohne Argument, das als ls -- .
behandelt wird (Listen Sie das aktuelle Verzeichnis auf), wenn keine Dateien übereinstimmen, was wahrscheinlich schlimmer ist, als ls
aufzurufen mit einem wörtlichen *.txt
als Argument.
Sie haben ähnliche Probleme mit den meisten Textdienstprogrammen:
grep foo *.txt
Würde nach foo
suchen auf stdin, wenn es kein txt
gibt Datei.
Eine vernünftigere Standardeinstellung, und zwar die von csh, tcsh, zsh oder fish 2.3+ (und von frühen Unix-Shells), besteht darin, den Befehl vollständig abzubrechen, wenn der Glob nicht übereinstimmt.
bash
(seit Version 3) hat einen failglob
Option dafür (interessant für diese Diskussion, da im Gegensatz zu ash
, AT&T ksh
oder zsh
, bash
unterstützt keine lokalen Gültigkeitsbereiche für Optionen (obwohl sich das in 4.4 ändern wird), diese Option macht, wenn sie global aktiviert ist, ein paar Dinge kaputt, wie die Bash-Vervollständigungsfunktionen).
Beachten Sie, dass sich csh und tcsh leicht von zsh
unterscheiden , fish
oder bash -O failglob
in Fällen wie:
ls -- *.txt *.html
Wo alle Globs nicht übereinstimmen müssen, damit der Befehl abgebrochen wird. Wenn es beispielsweise eine txt-Datei und keine html-Datei gibt, wird das zu:
ls -- file.txt
Sie können dieses Verhalten mit zsh
erreichen mit setopt cshnullglob
obwohl ein vernünftigerer Weg, es in zsh
zu tun wäre ein Glob wie:
ls -- *.(txt|html)
In zsh
und ksh93
, können Sie auch nullglob anwenden auf einer Pro-Glob-Basis, was ein viel vernünftigerer Ansatz ist, als eine globale Einstellung zu ändern:
files=(*.txt(N)) # zsh
files=(~(N)*.txt) # ksh93
würde ein leeres Array erstellen, wenn es kein txt
gibt Datei, anstatt den Befehl mit einem Fehler abzubrechen (oder es zu einem Array mit einer *.txt
wörtliches Argument mit anderen Shells).
Versionen von fish
vor 2.3 würde wie bash -O nullglob
funktionieren aber bei Interaktivität eine Warnung ausgeben, wenn ein Glob keine Übereinstimmung hat. Seit 2.3 funktioniert es wie zsh
außer Globs, die in for
verwendet werden , set
oder count
.
Nun, zur Historie, das Verhalten war tatsächlich kaputt durch die Bourne-Shell. In früheren Unix-Versionen erfolgte das Globbing über /etc/glob
Helfer und dieser Helfer verhielt sich wie csh
:Der Befehl würde fehlschlagen, wenn keiner der Globs mit einer Datei übereinstimmt, und andernfalls würden die Globs ohne Übereinstimmung entfernt.
Die Situation, in der wir uns heute befinden, ist also auf eine schlechte Entscheidung zurückzuführen, die in der Bourne-Shell getroffen wurde.
Beachten Sie, dass die Bourne-Shell (und die C-Shell) mit einem weiteren neuen Unix-Feature ausgestattet waren:der Umgebung. Das bedeutete Variablenerweiterung (der Vorgänger hatte nur den $1
, $2
… Positionsparameter). Die Bourne-Shell hat auch die Befehlssubstitution eingeführt.
Eine weitere schlechte Entwurfsentscheidung der Bourne-Shell bestand darin, bei der Erweiterung von Variablen und der Befehlsersetzung Globbing (und Splitting) durchzuführen (möglicherweise aus Gründen der Abwärtskompatibilität mit der Thompson-Shell, in der echo $1
würde immer noch /etc/glob
aufrufen wenn $1
enthaltene Platzhalter (es war dort eher eine Präprozessor-Makroerweiterung, da der erweiterte Wert erneut als Shell-Code geparst wurde)).
Fehlerhafte Globs, die nicht übereinstimmen, würden beispielsweise Folgendes bedeuten:
pattern='a.*b'
grep $pattern file
würde der Befehl fehlschlagen (es sei denn, es gibt einige a.whateverb
Dateien im aktuellen Verzeichnis). csh
(der auch Globbing bei der Variablenerweiterung durchführt) schlägt in diesem Fall fehl (und ich würde argumentieren, dass es besser ist, als einen schlafenden Fehler dort zu belassen, selbst wenn es nicht so gut ist, überhaupt kein Globbing zu machen, wie in zsh
).