Warum können bestimmte Programme (wie Readlink) keine Eingaben von einer Pipe annehmen?

Warum können bestimmte Programme (wie Readlink) keine Eingaben von einer Pipe annehmen?

Ich habe einen symbolischen Link zu einem Skript in meiner $PATHDatei, dessen Datei ich bearbeiten wollte. Ich habe den Dateipfad vergessen und habe Folgendes versucht:

$ which my_script_link | readlink

Ich hatte erwartet, dass der Dateipfad ausgegeben wird, aber stattdessen wurde

> readlink: missing operand
> Try 'readlink --help' for more information

Ich habe ein ähnliches Verhalten schon in anderen Situationen gesehen (z. B. beim Versuch, eine Liste von Dateien zum Bearbeiten in vim zu leiten). Ich weiß, dass es Workarounds gibt, wie z. B. eine Subshell readlink $(which my_script_link), aber ich möchte verstehenWarumdie Verrohrung funktioniert in dieser Situation nicht so, wie ich es mir vorstelle.

Danke!

Antwort1

Einfach ausgedrückt, weil das Programm nicht dafür geschrieben ist. Es liegt an den Programmierern, zu entscheiden, ob ihre Software von STDIN lesen kann oder ob sie eine Eingabedatei benötigt. Im Fall von heißt readlinkes auf mander Seite (Hervorhebung von mir):

Readlink - Drucken gelöstsymbolische Linksoder kanonischDateinamen

SYNOPSIS
Leselink [OPTION] …DATEI...

Als allgemeine Regel gilt, dass Programme, die Eingaben von STDIN entgegennehmen, so konzipiert sind, dass sie diese Eingaben irgendwie analysieren. Wenn Sie Daten an eine Pipe weiterleiten, readlinkempfängt diese einen Textstrom und weiß nicht, was sie damit anfangen soll, da sie nur mit Dateien und nicht mit deren Inhalten arbeitet. Dasselbe gilt für Programme wie lsoder cdusw. cpNur Programme, die mit Text-/Datenströmen arbeiten, können Eingaben von einer Pipe annehmen.

Antwort2

Ob ein Programm seine Eingaben von stdinoder als Befehlszeilenargumente erhält, bleibt dem Entwickler überlassen. Beide Ansätze haben ihre Vorteile. Bei Programmen, die ausschließlich mit Dateien arbeiten, ist es jedoch normalerweise bequemer, die Dateinamen als Befehlszeilenargumente zu übergeben, anstatt über stdin.

Der offensichtlichste Grund besteht darin, dass es im Normalfall, also beim Ausführen eines Programms auf einer Datei, einfacher ist, Folgendes einzugeben: readlink filestatt echo file | readlink.

Ein subtileres Problem ist die Korrektheit. Das Problem besteht darin, dass das Programm bei der Durchleitung von Dateinamen stdinin der Lage sein muss, einen Dateinamen von einem anderen zu unterscheiden. Dies geschieht häufig dadurch, dass angenommen wird, dass die Dateinamen durch Leerzeichen oder Zeilenumbrüche getrennt sind. Dies ist jedoch falsch, da Dateinamen Leerzeichen enthalten können. Eine bessere Möglichkeit besteht darin, die Dateinamen durch Nullbytes zu trennen. Dies kann jedoch umständlich sein, eine Liste von Dateien zu erstellen, die durch Nullen getrennt sind.

Das Übergeben von Dateinamen auf der Befehlszeile vermeidet dieses Problem, da die Shell die gesamte Analyse und Anführungszeichen für das Programm übernimmt. Sie können dies eingeben touch $'foo\nbar'und touches wird korrekt als ein Dateiname mit einer neuen Zeile angesehen, ohne dass Sie selbst spezielle Analysen oder Anführungszeichen durchführen müssen.

stdinWenn Sie dennoch Dateien für ein bestimmtes Programm durchleiten möchten , können Sie das tun. Dafür ist es xargsda. xargsDamit können Sie ein Programm nehmen, das nur Argumente auf der Befehlszeile akzeptiert, und es stattdessen dazu bringen, seine Argumente durchzuleiten stdin.

Mit anderen Worten, Sie können damit Folgendes tun: which my_script | xargs readlink.

Wenn Sie möchten, dass es immer readlinkauf diese Weise funktioniert, können Sie einen Alias ​​erstellen: alias readlink="xargs readlink". Damit können Sie which my_script | readlinkwie ursprünglich gewünscht eingeben.

Antwort3

Für Befehle, die keine Argumente über STDIN annehmen, können Sie stattdessen dieses Code-Pragma verwenden:

$ readlink $(which my_script_link)

Beispiel

$ ln -s /bin/ls ~/bin/somecmd

Überprüfen Sie nun, ob es aktiviert ist $PATH.

$ which somecmd
~/bin/somecmd

Oder die bevorzugte Methode, indem Sie typestatt folgendes verwenden which:

$ type somecmd
somecmd is /home/saml/bin/somecmd

Oder nur der Wert:

$ type -P somecmd 
/home/saml/bin/somecmd

Jetzt führen wir aus readlink:

$ readlink $(type -P somecmd)
/bin/ls

verwandte Informationen