Nach meinem Verständnis wird ein Glob-Platzhalter von der Shell interpretiert, die dann den angegebenen Befehl für jeden übereinstimmenden Dateinamen ausführt. Angenommen, ich habe Dateien:abc1, abc2, and abc3
in meinem aktuellen Verzeichnis. Dann zum Beispiel echo abc*
wird einmal für jeden Dateinamen wiederholt, der mit „abc“ beginnt.
Wenn ich jedoch grep 'foo' abc*
ausführe , ich stelle mir vor, das sollte laufen:
grep 'foo' abc1
grep 'foo' abc2
grep 'foo' abc3
Was bedeutet, dass ich die folgende Ausgabe erhalten sollte (vorausgesetzt, alle Dateien enthalten eine Zeile mit der Aufschrift „foo“):
foo
foo
foo
Stattdessen bekomme ich jedoch:
abc1:foo
abc2:foo
abc3:foo
Also ich denke, es gibt 2 mögliche Erklärungen dafür. Erstens kann grep irgendwie erkennen, dass es mit einem Glob-Ausdruck verwendet wurde, und antwortet, indem es die Dateinamen vor den Übereinstimmungen ausgibt. Zweitens führt die Shell, da Sie mehrere Dateien an grep übergeben können, tatsächlich nur einen Befehl aus:
grep 'foo' abc1 abc2 abc3
Dies funktioniert jedoch nur, weil grep am Ende mehrere Dateien akzeptiert. Es ist möglich, dass ein anderer Befehl nur die Übergabe einer Datei zulässt. Wenn Sie also den Befehl für mehrere Dateien ausführen möchten, die mit dem Glob übereinstimmen, würde es nicht funktionieren, wenn das Globbing über die zweite oben beschriebene Methode funktioniert.
Wie auch immer, kann jemand etwas Licht ins Dunkel bringen?
Danke!
Akzeptierte Antwort:
Das ist der Trick:Befehl weiß es nicht, es ist die Shell, die den Job macht
Betrachten Sie zum Beispiel grep 'abc' *.txt
. Wenn wir eine Ablaufverfolgung von Systemaufrufen ausführen, sehen Sie etwa Folgendes:
bash-4.3$ strace -e trace=execve grep "abc" *.txt > /dev/null
execve("/bin/grep", ["grep", "abc", "ADDA_converters.txt", "after.txt", "altera_license.txt", "altera.txt", "ANALOG_DIGITAL_NOTES.txt", "androiddev.txt", "answer2.txt", "answer.txt", "ANSWER.txt", "ascii.txt", "askubuntu-profile.txt", "AskUbuntu_Translators.txt", "a.txt", "bash_result.txt", ...], [/* 80 vars */]) = 0
+++ exited with 0 +++
Die Shell expandierte *.txt
in alle Dateinamen im aktuellen Verzeichnis, die auf .txt
enden Verlängerung. Ihre Shell übersetzt also effektiv die grep 'abc' *.txt
Befehl in grep 'abc' file1.txt file2.txt file3.txt . . .
. Somit ist Ihre zweite Annahme richtig.
Erste Annahme ist nicht richtig – Programme haben keine Möglichkeit, Glob zu erkennen. Es ist möglich, *
zu übergeben als String-Argument für den Befehl, aber es ist die Aufgabe des Befehls, zu entscheiden, was dann damit geschehen soll. Die Dateinamenerweiterung ist jedoch, wie ich bereits erwähnt habe, Eigentum Ihrer jeweiligen Shell.
Dies funktioniert jedoch nur, weil grep am Ende mehrere Dateien akzeptiert. Es ist möglich, dass ein anderer Befehl nur die Übergabe einer Datei zulassen würde.
Genau richtig ! Programme begrenzen die Anzahl der akzeptablen Befehlszeilenargumente nicht (in C ist das beispielsweise ein Array von Zeichenfolgen const char *args[]
und in Python sys.argv[]
), aber sie können die Länge erkennen dieses Arrays oder ob etwas Unerwartetes an der falschen Array-Position steht. grep
tut das nicht und akzeptiert mehrere Dateien, was beabsichtigt ist.
Nebenbei bemerkt, falsches Zitieren in Verbindung mit Globbing mit grep kann manchmal ein Problem sein. Bedenken Sie Folgendes:
bash-4.3$ echo "one two" | strace -e trace=execve grep *est*
execve("/bin/grep", ["grep", "self_test.sh", "test.wxg"], [/* 80 vars */]) = 0
+++ exited with 1 +++
Unvorbereitete Benutzer würden erwarten, dass grep jede Zeile mit est
findet Buchstaben darin, die aus der Pipe stammen, aber stattdessen verdrehte die Dateinamenerweiterung der Shell alles. Ich habe das oft bei Leuten gesehen, die ps aux | grep shell_script_name.sh
, und sie erwarten, dass ihr Prozess ausgeführt wird, sondern weil sie den Befehl aus demselben Verzeichnis ausgeführt haben, in dem sich das Skript befand , Shells Dateinamenerweiterung wurde zu grep
Befehl, hinter die Kulissen ganz anders zu schauen, als der Benutzer erwartet hat.
Der richtige Weg wäre, einfache Anführungszeichen zu verwenden:
bash-4.3$ echo "one two" | strace -e trace=execve grep '*est*'
execve("/bin/grep", ["grep", "*est*"], [/* 80 vars */]) = 0
+++ exited with 1 +++