Die Erweiterung mit * .txt in der Shell funktioniert nicht, wenn keine .txt-Datei vorhanden ist

10

Ich spielte mit Expansion herum und bemerkte ein merkwürdiges Verhalten. Ich habe versucht:

echo ./*.txt

Und ich hatte keine TXT-Datei in meinem aktuellen Verzeichnis. Die Ausgabe, die ich bekam, war:

./*.txt

Ich bin nur neugierig: Warum habe ich das bekommen? Ich hatte erwartet, keine Ausgabe zu bekommen.

PS: Als ich eine .txtDatei hatte, wurde die Erweiterung richtig interpretiert. Mit anderen Worten, sagen wir, ich hätte eine Datei smthn.txt, das Echo hallte tatsächlich wider current_directory/smthn.txt.

Ein ignoranter Wanderer
quelle

Antworten:

15

Anpassung von der Bash-Shell-Manpage,

bash durchsucht jedes Wort nach den Zeichen *,? und [. Wenn eines dieser Zeichen angezeigt wird, wird das Wort als Muster betrachtet und durch eine alphabetisch sortierte Liste von Dateinamen ersetzt, die dem Muster entsprechen. Wenn keine übereinstimmenden Dateinamen gefunden werden und die Shell-Option nullglob nicht aktiviert ist, bleibt das Wort unverändert. Wenn die Option nullglob festgelegt ist und keine Übereinstimmungen gefunden werden, wird das Wort entfernt.

In diesem Fall gehe ich davon aus, dass nullglob nicht aktiviert ist, sodass das Wort unverändert bleibt - daher die Ausgabe, die Sie sehen.

ColinB
quelle
2
Sie können das Verhalten wie folgt ändern: Es shopt -s nullglobwerden leere Zeichenfolgen für nicht übereinstimmende Muster und shopt -u nullglob(Standardeinstellung) das Muster selbst ausgegeben.
PerlDuck
13

Ich hatte erwartet, keine Ausgabe zu bekommen.

Wenn dies nullglobdie Standardeinstellung wäre, würden sich viele Befehle ganz unerwartet verhalten, da es (möglicherweise leider) üblich ist, dass Befehle den Fall von Null- Dateinamen-Argumenten qualitativ anders behandeln als den Fall von einem oder mehreren Dateinamen-Argumenten.

Angenommen, Sie haben aktiviert nullglob( shopt -s nullglob) und befinden sich in einem Verzeichnis, in dem keine Dateien übereinstimmen *.txt. Dann *.txtwird sich in der Tat zu nichts ausdehnen - kein leeres Feld, aber überhaupt keine Felder - wie Sie es erwartet haben. Aber das hätte folgende Ergebnisse:

  • ls *.txtwürde alle Dateien im aktuellen Verzeichnis auflisten (außer versteckte Dateien), da dies der lsFall ist, wenn Sie keine Dateinamenargumente übergeben.
  • cat *.txtwürde von der Standardeingabe lesen , denn wenn cates keine Dateinamenargumente gibt, ist es, als ob Sie ausgeführt würden cat -. Wenn es interaktiv ausgeführt wird, wartet es auf Eingaben. Viele Befehle verhalten sich so.
  • cp *.txt dest/würde mit dem Fehler scheitern cp: missing destination file operand after 'dest/'. Dies ist keine Katastrophe, aber es ist verwirrend und ganz anders als der stille Erfolg, der wahrscheinlich erwünscht ist.
  • file *.txtund verschiedene andere Programme ohne spezielles Verhalten für den Fall von Argumenten mit dem Dateinamen Null würden immer noch mit einer Fehler- oder Verwendungsmeldung fehlschlagen, wenn keine übergeben werden.
  • Selbst Fälle, die sich intuitiv so anfühlen, als sollten sie oft funktionieren, würden dies nicht tun. printf 'Got file: "%s"\n' *.txtwürde Got file: ""statt nichts drucken .
  • Versehentliches Versäumnis zu zitieren Vorkommen *, ?und [die sind nicht beabsichtigt , von dem Shell öfter offensichtlich falschen Ergebnisse produzieren würde erweitert werden, aber in einer Weise, um herauszufinden , könnte schwierig sein. Wenn beispielsweise keine Dateinamen im aktuellen Verzeichnis mit beginnen gedit, wird apt list gedit*(wo apt list 'gedit*'beabsichtigt) gerecht apt listund listet alle verfügbaren Pakete auf.

Es ist also gut, dass Sie dieses Verhalten nicht erhalten, ohne es anzufordern. Die wahrscheinlich häufigste praktische Situation, die tatsächlich durch vereinfacht wird, nullglobist for f in *.txt. Siehe auch diese Frage (mit der Sergiy Kolodyazhnyys Antwort verknüpft ist).

Die schwierigere Frage ist, warum - failglobwo es ein Erweiterungsfehler ist, einen Glob zu haben, der nicht mit Dateien übereinstimmt - nicht die Standardeinstellung in Bash ist. Ich glaube, Sergiy Kolodyazhnyys Antwort erfasst den Grund dafür, auch ohne ihn direkt anzusprechen. Nicht beibehaltene Globs beizubehalten, ohne einen Expansionsfehler zu erzeugen, ist (möglicherweise leider) das standardisierte Verhalten, und es ist auch traditionelles und daher erwartetes Verhalten. Obwohl bash nicht versucht, vollständig POSIX-kompatibel zu sein, es sei denn, es wird mit dem Namen aufgerufen shoder die --posixOption übergeben, folgen viele seiner Entwurfsoptionen, auch wenn es sich nicht im POSIX-Modus befindet, direkt POSIX. Sie mussten sich für ein Verhalten entscheiden, und es gibt Nachteile, die damit verbunden sind, den Erwartungen der Benutzer zu widersprechen.


Ich denke, dies ist der am wenigsten historisch einflussreiche Aspekt der Angelegenheit, also habe ich ihn zum letzten Mal gespeichert ... aber es ist erwähnenswert, dass nullglobVerhalten konzeptionell etwas Seltsames ist .

nullgloberscheint auf den ersten Blick elegant, da syntaktisch der Fall von null übereinstimmenden Dateien nicht anders behandelt wird als der Fall von eins, zwei oder einer anderen Zahl. Die von uns ausgeführten Befehle, für die Globs zu Argumenten erweitert werden , behandeln sie normalerweise nicht gleich, wie oben beschrieben. Aber syntaktisch fühlt sich das zumindest richtig an, was meiner Meinung nach die Motivation für Ihre Frage ist.

Und doch gibt es eine andere, subtilere Inkonsistenz, nullglobdie nicht angesprochen wird - die sie tatsächlich verstärkt. Der Fall von Null-Globbing-Zeichen ("Platzhalter") wird grundlegend anders behandelt als der von einer, zwei oder einer anderen Zahl. Zum Beispiel mit shopt -s nullglob, wenn ab?d?fkeine Dateien übereinstimmen, wird es entfernt; Wenn ab?dkeine Dateien übereinstimmen, werden sie entfernt. Wenn abjedoch keine Dateien übereinstimmen (dh wenn keine Datei vorhanden ist, deren Name genau lautet ab), wird sie immer noch nicht entfernt. Natürlich wäre es eine Katastrophe, wenn es entfernt würde, da es möglicherweise überhaupt nicht beabsichtigt ist, auf eine vorhandene Datei im aktuellen Verzeichnis zu verweisen. Es verweist möglicherweise nicht einmal auf eine Datei. Damit entfällt jedoch die Hoffnung auf völlige Beständigkeit.

Die drei Verhaltensweisen, die Bash bietet - die Standardeinstellung, Globs, die nicht mit Dateien übereinstimmen, so zu behandeln, als wären sie keine Globs, und sie nicht zu erweitern, das Verhalten, das Sie von ihrer Behandlung erwartet haben (wenn Sie diese seltsame Wendung verzeihen) Dies bedeutet, dass alle Nullen der übereinstimmenden Dateien ( nullglob) und das sichere Verhalten, sie als Fehler zu betrachten ( failglob), unterschiedliche Ansätze für die der Shell innewohnende Mehrdeutigkeit darstellen, die nicht wissen kann, ob ein bestimmtes Wort a sein soll Dateiname. Die Shell führt ihre Erweiterungen durch, ohne zu wissen, wie die von ihr aufgerufenen Befehle ihre Argumente behandeln.

Dies ist einer von vielen Fällen der Trennung von Bedenken . In Systemen, deren Design der Unix-Philosophie folgt, soll jedes Teil eines tun und es gut machen . Die Shell verarbeitet Text in Befehle und Argumente und ruft die Befehle auf, von denen die meisten außerhalb der Shell selbst liegen. Dies ist in der Regel viel schöner und vielseitiger als Systeme, bei denen die externen Befehle selbst für die Durchführung dieser Transformationen verantwortlich sind (wie bei den herkömmlichen Befehlsprozessoren unter DOS und Windows). Aber es hat gelegentlich Nachteile.

Eliah Kagan
quelle
Anscheinend hat bash-4.3.39 (2) nicht failglob. Es kann also nicht die Standardeinstellung sein, da es nicht immer unterstützt wurde.
Ruslan
6

Der Hauptgrund ist , weil dies ist das Standardverhalten von bestimmtem POSIX - der Standard , die Abdeckungen Kommandosprache Shell und unter anderem Mustervergleich (Muscheln wie bash, dashShell - Ubuntu standardmäßig /bin/sh, und kshfolgen Sie diesem Standard). Ab Abschnitt 2.13.3 Muster für die Dateinamenerweiterung :

Wenn das Muster nicht mit vorhandenen Dateinamen oder Pfadnamen übereinstimmt, bleibt die Musterzeichenfolge unverändert.

Dies hat natürlich einen Nebeneffekt - passenden Dateinamen, der buchstäblich sein kann *.txt. Die nullglobOption in bashund zshkann helfen: Wenn diese Option über aktiviert ist shopt -s nullglob(und standardmäßig nicht aktiviert ist, was für diese Frage gilt), wird globstar auf eine leere Zeichenfolge erweitert, wenn keine passenden Dateinamen gefunden werden. ksh93hat einen eigenen fortschrittlichen Mustervergleichsmechanismus, der den gleichen Effekt erzielt~(N)*.txt

Siehe auch Warum ist nullglob nicht standardmäßig?

Sergiy Kolodyazhnyy
quelle