Ich habe einen symbolischen Link zu einem Skript in meiner $PATH
Datei, 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 readlink
es auf man
der 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, readlink
empfä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 ls
oder cd
usw. cp
Nur Programme, die mit Text-/Datenströmen arbeiten, können Eingaben von einer Pipe annehmen.
Antwort2
Ob ein Programm seine Eingaben von stdin
oder 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 file
statt echo file | readlink
.
Ein subtileres Problem ist die Korrektheit. Das Problem besteht darin, dass das Programm bei der Durchleitung von Dateinamen stdin
in 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 touch
es wird korrekt als ein Dateiname mit einer neuen Zeile angesehen, ohne dass Sie selbst spezielle Analysen oder Anführungszeichen durchführen müssen.
stdin
Wenn Sie dennoch Dateien für ein bestimmtes Programm durchleiten möchten , können Sie das tun. Dafür ist es xargs
da. xargs
Damit 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 readlink
auf diese Weise funktioniert, können Sie einen Alias erstellen: alias readlink="xargs readlink"
. Damit können Sie which my_script | readlink
wie 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 type
statt 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