readlinkなどの一部のプログラムがパイプから入力を受け取れないのはなぜですか?

readlinkなどの一部のプログラムがパイプから入力を受け取れないのはなぜですか?

$PATH編集したいファイルのスクリプトを指すシンボリックリンクがあります。ファイルパスを忘れて、次のことを試してみました。

$ which my_script_link | readlink

出力ファイルへのパスが予想されましたが出力されます。

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

以前は、他の状況(編集用にファイルリストをvimにパイプするなど)でも同様の動作を見たことがあります。 subshel​​lのような回避策があることを知っていますが、readlink $(which my_script_link)理解したいと思います。なぜこの場合、パイプラインは私が想像したように動作しません。

ありがとうございます!

答え1

簡単に言えば、これを行うようにプログラムが作成されていないからです。ソフトウェアがSTDINから読み取れるかどうか、または入力ファイルが必要かどうかを判断するのはプログラマーの役割です。この場合、readlinkそのmanページには次のように指定されています(強調表示)。

readlink - 印刷が解決されましたシンボリックリンクまたは規範的なファイル名

要約
読み取りリンク[オプション] ...文書...

通常、STDINから入力を受け取るプログラムは、どのような方法でもその入力を解析するように設計されています。データをパイプするとreadlinkテキストストリームを受け取りますが、コンテンツではないファイルのみを処理するため、どうすればよいかわかりません。lsまたはのcdようなプログラムも同じですcp。テキスト/データストリームを処理するプログラムだけがパイプの入力を受け入れることができます。

答え2

プログラムがstdinコマンドライン引数から入力を受け取るのか、コマンドライン引数として入力を受け取るのかは、デザイナーによって異なります。どちらの方法にも利点があります。しかし、ファイルを厳密に操作するプログラムの場合、ファイル名をコマンドライン引数として渡すのが一般的ですstdin

最も明白な理由は、ファイルからプログラムを実行する一般的なケースでは 。readlink file代わりに:を入力する方が簡単ですecho file | readlink

より微妙な問題は正確さです。問題は、ファイル名を経由して渡されると、プログラムがあるstdinファイル名を別のファイル名と区別できる必要があることです。通常、これはファイル名がスペースまたは改行文字で区切られていると仮定して実行されますが、ファイル名にスペースを含めることができるため、これは正しくありません。より良いアプローチは、nullバイトを使用してファイル名を区切ることですが、nullバイトで区切られたファイルのリストを生成するのは不便です。

コマンドラインにファイル名を渡すと、シェルはプログラム内のすべての解析と参照を処理するため、この問題を回避できます。これを入力すると、touch $'foo\nbar'特別なtouch解析や引用そのものを処理することなく、改行文字を含むファイル名で正しく処理されます。

つまり、stdin特定のプログラムのファイルを渡したい場合は、そうすることができます。それがxargs目的です。xargsコマンドラインの引数だけを受け入れ、それを渡すプログラムを使用できますstdin

つまり、次のことができますwhich my_script | xargs readlink

常にこの方法で作業するには、readlinkエイリアスを作成できますalias readlink="xargs readlink"。これにより、もともと望んだ方法で入ることができますwhich my_script | readlink

答え3

STDINを介して引数を取得しないコマンドの場合は、次のコードプラグマを使用できます。

$ readlink $(which my_script_link)

はい

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

今そこにあることを確認してください$PATH

$ which somecmd
~/bin/somecmd

または、好む方法は、type次の代わりに使用することですwhich

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

または単に値:

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

次に、次を実行しますreadlink

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

関連情報