Warum lesen einige Befehle nicht von ihrer Standardeingabe?


19

Ich frage mich, wann wir Pipeline verwenden sollten und wann nicht.

Sagen wir zum Beispiel, um einen bestimmten Prozess abzubrechen, der mit PDF-Dateien umgeht, funktioniert Folgendes nicht mit Pipeline:

ps aux | grep pdf | awk '{print $2}'|kill

Stattdessen können wir es nur auf folgende Arten tun:

kill $(ps aux| grep pdf| awk '{print $2}')

oder

ps aux | grep pdf | awk '{print $2}'| xargs kill

Laut man bash(version 4.1.2):

The standard output of command is connected via a pipe to the standard input of command2.

Für obiges Szenario:

  • der stdin von grepist der stdout von ps. Das funktioniert.
  • der stdin von awkist der stdout von grep. Das funktioniert.
  • der stdin von killist der stdout von awk. Das geht nicht

Das stdin des folgenden Befehls wird immer vom stdout des vorherigen Befehls eingegeben.

  • Warum funktioniert es nicht mit killoder rm?
  • Was ist der Unterschied zwischen kill, rmEingabe mit grep, awkEingabe?
  • Gibt es irgendwelche Regeln?

1
Das ist keine Antwort , aber Sie vielleicht einen Blick auf die haben wollen pgrep, pkillund killallBefehle.
Terdon

2
@terdon: Ich benutze nur das obige Szenario, um das Pipeline-Problem zu zeigen, ich verstehe das pgrepund der Rest kann dies perfekt erreichen :)
sylye

Antworten:


17

Es gibt zwei übliche Möglichkeiten, um Eingaben für Programme bereitzustellen:

  • STDIN Daten der Prozesse zur Verfügung stellen
  • Befehlszeilenargumente angeben

killVerwendet nur Befehlszeilenargumente. Es wird nicht aus STDIN gelesen. Programme mögen grepund awklesen von STDIN (wenn keine Dateinamen als Befehlszeilenargumente angegeben sind) und verarbeiten die Daten gemäß ihren Befehlszeilenargumenten (Muster, Anweisungen, Flags, ...).

Sie können nur zu STDIN anderer Prozesse leiten, nicht zu Befehlszeilenargumenten.

Die allgemeine Regel ist, dass Programme STDIN verwenden, um eine beliebige Datenmenge zu verarbeiten. Alle zusätzlichen Eingabeparameter oder, wenn es normalerweise nur wenige gibt, werden von Befehlszeilenargumenten übergeben. Wenn die Kommandozeile sehr lang werden kann, zB bei langen awkProgrammtexten, besteht häufig die Möglichkeit, diese aus zusätzlichen Programmdateien zu lesen ( -fOption von awk).

Verwenden Sie $(...)oder bei vielen Daten, um das STDOUT von Programmen als Befehlszeilenargumente zu verwenden xargs. findkann auch das direkt mit -exec ... {} +.

Der Vollständigkeit halber: Um Befehlszeilenargumente in STDOUT zu schreiben, verwenden Sie echo.


1
Woher wissen wir, dass ein Befehl nur Argumente akzeptiert, nicht aber STDIN? Gibt es einen systematischen oder programmatischen Weg, anstatt zu raten oder aus der Manpage zu lesen? Indem ich nur die Manpage las, konnte ich keine konkreten Hinweise darauf erhalten, ob der Befehl STDIN annehmen kann oder nicht, da STDIN auch Teil der Argumente aus der Art und Weise ist, wie eine Manpage dargestellt wird. Beispielsweise wurde gzipin der ÜBERSICHT nicht angegeben, dass ein DATEINAME als Eingabe verwendet werden muss. Ich schaue, gibt es einen systematischeren Weg, um das festzustellen.
Sylye

Es gibt auch das Argument "-", das für einige Befehle "stdin" (oder "stdout") bedeutet.
Emmanuel

Erlauben Sie nicht xargsgenau, "Argumente an Befehlszeile weiterzuleiten"?
T. Verron

@ T.Verron ja, das ist die aufgabe von xargs. Es ruft den Befehl bei Bedarf mehrmals auf (die Befehlszeilengröße ist begrenzt) und hat viele andere Optionen.
Jofel

2
Der Text der Beschreibung beschreibt, wie Sie das Programm verwenden können. Beispielsweise sagt gzip: "Das Programm gzip komprimiert und dekomprimiert Dateien mit der Lempel-Ziv-Codierung (LZ77). Wenn keine Dateien angegeben sind, komprimiert gzip von der Standardeingabe oder dekomprimiert auf die Standardausgabe." Wenn eine Manpage keine Standardeingabe enthält, wird sie nicht verwendet.
Alan Shutko

16

Dies ist eine interessante Frage, die sich mit einem Teil der Unix / Linux-Philosophie befasst.

Also, was ist der Unterschied zwischen Programmen wie grep, sed, sortauf der einen Seite und kill, rm, lsauf der anderen Seite? Ich sehe zwei Aspekte.

Der Filter Aspekt

  • Die erste Art von Programmen nennt man auch Filter . Sie nehmen eine Eingabe, entweder aus einer Datei oder aus STDIN, ändern diese und generieren eine Ausgabe, meist nach STDOUT. Sie sollen in einer Pipe mit anderen Programmen als Quellen und Ziele verwendet werden.

  • Die zweite Art von Programmen wirkt auf eine Eingabe, aber die Ausgabe, die sie geben, hängt oft nicht mit der Eingabe zusammen. killhat keine Ausgabe, wenn es regelmäßig funktioniert, auch nicht ls. Sie haben nur einen Rückgabewert, um Erfolg zu zeigen. Sie nehmen normalerweise keine Eingabe von STDIN entgegen, geben aber meistens eine Ausgabe an STDOUT aus.

Bei Programmen wie diesem lsfunktioniert der Filteraspekt nicht so gut. Es kann durchaus einen Eingang haben (benötigt aber keinen), und der Ausgang ist eng mit diesem verbunden, funktioniert aber nicht als Filter. Für diese Art von Programmen funktioniert jedoch noch der andere Aspekt:

Der semantische Aspekt

  • Für Filter hat ihre Eingabe keine semantische Bedeutung . Sie lesen nur Daten, ändern Daten, geben Daten aus. Es spielt keine Rolle, ob es sich um eine Liste mit numerischen Werten, Dateinamen oder HTML-Quellcode handelt. Die Bedeutung dieser Daten ergibt sich nur aus dem Code, den Sie dem Filter geben: der Regex für grep, die Regeln für awkoder das Perl-Programm.

  • Für andere Programme wie killoder lshat ihre Eingabe eine Bedeutung , eine Bezeichnung . killerwartet Prozessnummern, lserwartet Datei- oder Pfadnamen. Sie können nicht mit beliebigen Daten umgehen und sind auch nicht dazu gedacht. Viele von ihnen benötigen nicht einmal Eingaben oder Parameter wie ps. Sie lesen normalerweise nicht aus STDIN.

Man könnte diese beiden Aspekte wahrscheinlich kombinieren: Ein Filter ist ein Programm, dessen Eingabe keine semantische Bedeutung für das Programm hat.

Ich bin mir sicher, dass ich irgendwo etwas über diese Philosophie gelesen habe, aber ich kann mich im Moment an keine Quellen erinnern, sorry. Wenn jemand Quellen hat, können Sie diese gerne bearbeiten.


5

Es gibt keine "Regeln" als solche. Einige Programme nehmen Eingaben von STDIN entgegen, andere nicht. Wenn ein Programm Eingaben von STDIN entgegennehmen kann, kann es weitergeleitet werden, wenn nicht, kann es nicht.

Normalerweise können Sie feststellen, ob ein Programm Eingaben akzeptiert oder nicht, indem Sie darüber nachdenken, was es tut. Wenn die Aufgabe des Programms ist es , irgendwie die zu manipulieren Inhalt einer Datei (zB grep, sed, awketc.), dauert es normalerweise eine Eingabe von STDIN. Wenn seine Aufgabe ist es, die Datei selbst (zB zu manipulieren mv, rm, cp) oder ein Verfahren (zB kill, lsof) oder Rückkehr Informationen über etwas (zB top, find, ps) , dann tut es nicht.

Eine andere Art, darüber nachzudenken, ist der Unterschied zwischen Argumenten und Eingaben. Beispielsweise:

mv foo bar

Hat im obigen Befehl mvkeine Eingabe als solche. Was gegeben wurde, sind zwei Argumente. Es weiß oder kümmert sich nicht darum, was sich in einer der Dateien befindet, es weiß nur, dass dies die Argumente sind, und es sollte sie manipulieren.

Auf der anderen Seite

sed -e 's/foo/bar/' < file
--- -- ------------   ----
 |   |       |          |-> input
 |   |       |------------> argument        
 |   |--------------------> option/flag/switch
 |------------------------> command

Hier wurde sedInput sowie ein Argument gegeben. Da es Eingaben akzeptiert, kann es diese von STDIN lesen und weitergeleitet werden.

Es wird noch komplizierter , wenn ein Argument sein , der Eingang. Beispielsweise

cat file

Hier fileist das Argument, das gegeben wurde cat. Um genau zu sein, der Dateiname file ist das Argument. Da es sich jedoch catum ein Programm handelt, das den Inhalt von Dateien manipuliert, erfolgt die Eingabe nach dem Inhalt file.

Dies kann mit straceeinem Programm veranschaulicht werden , das die von Prozessen ausgeführten Systemaufrufe nachverfolgt. Wenn wir cat foovia ausführen strace, können wir sehen, dass die Datei foogeöffnet ist:

$ strace cat foo 2| grep foo
execve("/bin/cat", ["cat", "foo"], [/* 44 vars */]) = 0
open("foo", O_RDONLY)     

Die erste Zeile oben zeigt, dass das Programm /bin/cataufgerufen wurde und seine Argumente catund waren foo(das erste Argument ist immer das Programm selbst). Später wurde das Argument fooim schreibgeschützten Modus geöffnet. Vergleichen Sie dies nun mit

$ strace ls foo 2| grep foo 
execve("/bin/ls", ["ls", "foo"], [/* 44 vars */]) = 0
stat("foo", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
lstat("foo", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
write(1, "foo\n", 4foo

Auch hier lsnahm sich und fooals Argumente. Es gibt jedoch keinen openAufruf, das Argument wird nicht als Eingabe behandelt. Ruft stattdessen lsdie Systembibliothek auf stat(die nicht mit dem statBefehl identisch ist ), um Informationen über die Datei abzurufen foo.

Zusammenfassend kann gesagt werden, dass Sie, wenn der von Ihnen ausgeführte Befehl seine Eingabe liest, eine Pipe zu ihm ausführen können. Wenn dies nicht der Fall ist, können Sie dies nicht.


0
  • Warum funktioniert es nicht mit kill oder rm?

killund rmbrauche kein STDIN.

  • Was ist der Unterschied zwischen kill, rm input und grep, awk input?

Für killund geben rmBenutzer ihre benutzerdefinierten Informationen als Argument an und $(cmd)helfen dabei, das STDOUT des cmdinfo-Arguments zu nehmen und es zu konvertieren.

Für grepund geben awkBenutzer Argumente und zusätzlich auch STDINeine reguläre Datei an, die vom Befehl verarbeitet wird. STDINKann mit Pipeline |oder durch manuelle Eingabe übergeben werden.

  • Gibt es irgendwelche Regeln?

Lesen Sie das Handbuch oder die Quellcodes. Und wenn Sie nichts finden, was Sie brauchen, können Sie einen einfachen, aber möglicherweise gefährlichen Test durchführen:

Geben Sie einfach den Befehl ein, auf den Sie neugierig sind, mit Argumenten, die Sie bereits verstanden haben, und prüfen Sie, ob der Befehl pausiert (es passiert nichts). Wenn es anhält, wartet es tatsächlich für STDIN (können Sie versuchen , catund echodie verschiedenen zu sehen). Sie geben manuell ein Ctrl-Dund der Befehl wird ausgeführt (Ergebnisse oder Fehler anzeigen) und kehrt zurück. Ein solcher Befehl benötigt in dieser Situation STDIN (mit Argumenten, die Sie angeben).

Der gleiche Befehl benötigt in verschiedenen Situationen möglicherweise nicht STDIN (z. B. catwartet auf STDIN, aber cat file.txtnicht).

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.