GNU/Linux >> LINUX-Kenntnisse >  >> Ubuntu

Woher weiß ein Befehl (z. B. Grep), wann er als Teil der Glob-Erweiterung ausgeführt wird?

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.

Verwandte:Nach dem Upgrade auf Ubuntu 13.10 stürzt Firefox manchmal den Computer ab?

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 +++

Ubuntu
  1. Wie führen wir einen in einer Variablen gespeicherten Befehl aus?

  2. Wie wird ein Befehl ausgeführt, wenn der Inhalt eines Verzeichnisses aktualisiert wird?

  3. Wie führe ich den ursprünglichen Befehl mit dem gleichen Namen aus?

  4. Wie führe ich eine Anwendung für eine festgelegte Zeit in der Shell aus?

  5. Wie kann sichergestellt werden, dass Wine .exe-Dateien nicht automatisch ausführt?

WIE MAN DEN SCHWANZBEFEHL VERWENDET

Wie man den Head-Befehl verwendet

So verwenden Sie grep unter Linux

So verwalten Sie Snaps unter Linux – Teil 2

Was macht der Startx-Befehl?

Was ist der Grep-Befehl unter Linux? Warum wird es verwendet und wie funktioniert es?