特定のプログラム (readlink など) がパイプから入力を取得できないのはなぜですか?

特定のプログラム (readlink など) がパイプから入力を取得できないのはなぜですか?

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

$ which my_script_link | readlink

ファイルパスが出力されると思っていましたが、代わりに出力されました

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

以前にも似たような動作を他の状況で見たことがあります(編集のためにファイルのリストをvimにパイプしようとするなど)。サブシェルのような回避策があることは知っていますreadlink $(which my_script_link)が、理解したいのはなぜこの状況では、パイピングは思った通りには機能しません。

ありがとう!

答え1

簡単に言えば、プログラムがそうするように書かれていないからです。ソフトウェアが STDIN から読み取ることができるか、入力ファイルが必要かどうかを決めるのはプログラマー次第です。 の場合readlink、そのmanページには次のように書かれています (強調は筆者による):

readlink - 解決済みの印刷シンボリックリンクまたは正規ファイル名

概要
readlink [オプション]...ファイル...

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

答え2

プログラムが入力を から受け取るかstdin、コマンドライン引数として受け取るかは、設計者次第です。どちらのアプローチにも利点があります。ただし、ファイルのみを操作するプログラムの場合は、 ではなくコマンドライン引数としてファイル名を渡す方が通常は便利ですstdin

最も明白な理由は、一般的なケース、つまりファイルに対してプログラムを実行するだけのケースでは、readlink fileではなく と入力する方が簡単だからですecho file | readlink

より微妙な問題は正確性です。問題は、ファイル名が に渡されるときにstdin、プログラムがファイル名を区別できる必要があることです。多くの場合、これはファイル名が空白または改行で区切られていると想定して行われますが、ファイル名には空白が含まれる可能性があるため、これは正しくありません。より良い方法は、ファイル名をヌルバイトで区切ることですが、ヌルで区切られたファイルのリストを生成するのは不便です。

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

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

関連情報