なぜコマンド「ls | ファイル」の動作?


32

私はコマンドラインについて研究してきましたが、|(パイプライン)はコマンドからの出力を別のコマンドの入力にリダイレクトすることを意味しています。では、なぜコマンドls | fileが機能しないのですか?

file 入力は次のようなファイル名の1つです file filename1 filename2

ls出力はフォルダ上のディレクトリとファイルのリストなのでls | file、フォルダ上のすべてのファイルのファイルタイプを表示することになっていると思いました。

ただし、使用すると、出力は次のようになります。

    Usage: file [-bcEhikLlNnprsvz0] [--apple] [--mime-encoding] [--mime-type]
        [-e testname] [-F separator] [-f namefile] [-m magicfiles] file ...
    file -C [-m magicfiles]
    file [--help]

fileコマンドの使用に何らかのエラーがあったため


2
plainを使用している場合ls、現在のディレクトリ内のすべてのファイルをfileコマンドで処理することを示します。...では、単に:を実行しないでください。file *これは、すべてのファイル、フォルダーの行で応答します。
クヌードラーセン

file *が最も賢い方法lsです。出力の使用が機能しない理由を疑問に思っていました。
疑わしい

6
前提には欠陥があります:「ファイル入力はファイルfilename1 filename2のようなファイル名の1つです」それは入力ではありません。@John Kugelmanが以下で指摘しているように、これらはコマンドライン引数です。
モンティハーダー

3
接線方向では、一般に解析lsは悪い考えです。
小次郎

回答:


71

基本的な問題は、file標準入力ではなく、コマンドライン引数としてファイル名を想定していることです。を記述ls | fileすると、の出力がにls入力として渡されますfile。引数としてではなく、入力として。

違いは何ですか?

  • コマンドライン引数は、コマンドの後にフラグやファイル名を書き込むときですcmd arg1 arg2 arg3。シェルスクリプトでは、これらの引数は変数として利用可能です$1$2$3あなたが経由でそれらにアクセスしたい、などでC char **argvおよびint argc引数へmain()

  • 標準入力のstdinは、データのストリームです。コマンドライン引数が与えられていない場合に、標準入力のようなプログラムcatまたはwc標準入力から読み取るプログラムもあります。シェルスクリプトではread、1行の入力を取得するために使用できます。C では、さまざまなオプションの中でscanf()またはを使用できgetchar()ます。

file通常、stdinからは読み取りません。少なくとも1つのファイル名が引数として渡されることを想定しています。ls | file引数を渡さなかったため、を記述するときに使用法を出力するのはこのためです。

のようxargsに、stdinを引数に変換するために使用できますls | xargs file。それでも、terdonが言及しているように、解析lsは悪い考えです。これを行う最も直接的な方法は単純です:

file *

2
またはfile、を使用して、入力からファイル名を強制的に取得しますls | file -f -。まだ悪い考えです。
スペクトル

2
@Braiam>それがポイントです。そして、それはlsの出力をfileの標準入力にパイプします。やってみて。
スペクトル

4
@Braiam>実際、それは無駄で危険です。しかし、それは機能します。OPがリダイレクトの使用を学習している場合は、より良いオプションと比較できると便利です。完全を期すためにfile $(ls)、また別の方法で機能するに言及することもできます。
スペクトル

2
すべての答えを読んだ後、私は問題の全体像を把握できたと思いますが、すべてを本当に理解するにはさらに読む必要があると思います。まず、明らかにパイピングとリダイレクトの使用は、出力を引数として解析するのではなく、STDINとして解析します。理解を深めるためにさらに読む必要がありますが、表面的な検索引数を作成すると、テキストが配列内のプログラムに解析されているように見え、STDINはファイルまたは出力の情報をプールする方法のようです(すべてのプログラムがこの「プーリング」で動作します)
-IanC

3
第二に、lsを使用してファイル名のリストを作成することは、ファイル名で受け入れられるがlsで誤解を招く出力になる特殊文字のために、悪い考えのように思えます。ファイル名間の区切り文字として改行を使用するため、ファイル名には改行やその他の特殊文字を含めることができるため、最終的な出力は正確ではない場合があります。
-IanC

18

あなたが言うように、の入力はfilenamesfileなければならないからです。ただし、の出力は単なるテキストです。ファイル名のリストであっても、それが単なるテキストであり、ハードドライブ上のファイルの場所ではないという事実は変わりません。ls

画面に出力が印刷されると、表示されるのはテキストです。そのテキストが詩であるか、ファイル名のリストであるかは、コンピューターにとって違いはありません。知っているのは、それがテキストであることだけです。これが、lsテキストとして入力を受け取るプログラムにの出力を渡すことができる理由です(ただし、実際にはそうすべきではありません)。

$ ls / | grep etc
etc

そのため、ファイル名を取得するコマンドの入力として、ファイル名をテキスト(lsまたはなどfind)としてリストするコマンドの出力を使用するには、いくつかのトリックを使用する必要があります。これの典型的なツールはxargs次のとおりです。

$ ls
file1 file2

$ ls | xargs wc
 9  9 38 file1
 5  5 20 file2
14 14 58 total

前にも言ったように、あなたは本当にの出力を解析したくありませんls。以下のようなものがfind良いです(print0印刷物\0の代わりに、各ファイル名の後にnewilneと-0のはxargs、それは、そのような入力に対処することができます。これは、ファイル名は改行を含むとあなたのコマンドを機能させるためのトリックです):

$ find . -type f -print0 | xargs -0 wc
 9  9 38 ./file1
 5  5 20 ./file2
14 14 58 total

これには、xargsまったく必要なく、これを行う独自の方法もあります:

$ find . -type f -exec wc {} +
 9  9 38 ./file1
 5  5 20 ./file2
14 14 58 total

最後に、シェルループを使用することもできます。ただし、ほとんどの場合、xargsはるかに高速で効率的になります。例えば:

$ for file in *; do wc "$file"; done
 9  9 38 file1
 5  5 20 file2

サイドの問題はつまりfile、明示的な与えられた場合を除き、実際に標準入力の読み取りには見えない-プレースホルダ:比較file fooecho foo | fileecho foo | file -。実際、これがおそらくOPの場合の使用法メッセージの理由です(つまり、実際にはの出力lsが「単純なテキスト」であるためではなく、むしろ引数リストfileが空であるため)
-steeldriver

@steeldriverええ。知る限りでは、入力としてテキストではなくファイルを必要とするすべてのプログラムが該当します。デフォルトでは標準入力を無視します。echo foo | file -実際fileにはファイルではfooなく、stdinストリームで実行されることに注意してください。
テルドン

さて、catstdinを-除いて、ファイル引数を指定した場合を除いて、そのような奇妙なアヒル(?!)がありますか?
スチールドライバー

3
この答えは、stdinとコマンドライン引数の違いを説明できないため、受け入れられた答えよりもポイントが多いにもかかわらず、同じ理由で依然として誤解を招きます。
-zwol

5
@terdonこの場合、それは重大なエラーだと思います。「file(1)は、標準入力としてではなく、コマンドライン引数として操作するファイルのリストを取ります」OPのコマンドが機能しなかった理由を理解するための基本であり、その区別は一般にシェルスクリプトの基本です。あなたはそれをつや出しすることによって彼らに何の恩恵も与えていません。
-zwol

6

「|」ということを学びました (パイプライン)は、コマンドからの出力を別のコマンドの入力にリダイレクトするためのものです。

出力は「リダイレクト」されませんが、プログラムの出力を受け取り、入力として使用します。一方、ファイルは入力ではなくファイル名を引数として受け取り、テストされます。リダイレクトでは、これらのファイル名は引数として渡されず、パイプでも引数として渡されません。

あなたができることは、--files-fromテストしたいすべてのファイルをリストするファイルがある場合、オプションでファイルからファイル名を読み取ることです。そうでなければ、単にファイルへのパスを引数として渡します。


6

受け入れられた答えは、パイプコマンドがすぐに機能しない理由を説明し、file *コマンドを使用すると、シンプルで簡単なソリューションを提供します。

いつか便利になるかもしれない別の代替案を提案したいと思います。トリックはバックティック(`)文字を使用しています。バックティックについては、ここで詳しく説明します。つまり、バッククォートで囲まれたコマンドの出力を取得し、それを文字列として残りのコマンドに置き換えます。

そのfind `ls`ため、lsコマンドの出力を取得し、findコマンドの引数として置き換えます。これは、受け入れられているソリューションよりも長く複雑です。ただし、他の状況ではこのバリエーションが役立つ場合があります。


私はLinuxでコマンドラインを使用することに関する本を読んでいます(疑問は私がそれを実験することから来ました)。あなたはどちらかを使用することができます$(コマンド)commandbashでコマンドの出力を拡張し、他のコマンドにパラメータとしてそれを使用するために(自分の携帯電話のバックスラッシュコードを見つけることができません)。この場合(lsを使用)で使用すると、一部のファイル名に特殊文字が含まれるため、依然としていくつかの問題が発生しますが、本当に便利です。
IanC 16

@IanC残念ながら、bashについてのほとんどの本やチュートリアルはゴミであり、悪い慣習、非推奨の構文、微妙なバグで汚染されています。(唯一の)信頼できるリファレンスはbash開発者、つまりfreenode のマニュアル#bash IRCチャネルです(チャネルトピックにリンクされているリソースも確認してください)。
イグニス

1
コマンド置換を使用することは時には非常に役立ちますが、このコンテキストでは、特にlsではかなりひねくれています。
ジョー


5

lsパイプ経由の出力は、各行を区切る0x0aのデータのブロック(つまり、改行文字)でありfile、これを1つのパラメーターとして取得します。

一般的なルールとして、決してls他のコマンドのデータソースを生成するために使用しないでください-いつか..にパイプされrmて、あなたは困っています!

ループを使用するfor i in *; do file "$i" ; doneことをお勧めします。これにより、必要な出力が予想どおりに生成されます。スペースを含むファイル名の場合、引用符があります。


8
簡単:file *;-)
Wayne_Yux

3
@IanC私は実際の出力を解析することを十分に強調することはできませんls非常に、非常に悪い考え。それは、などの有害なものに渡す可能性があるだけでなくrm、より重要なのは、非標準のファイル名で破損するためです。
テルドン

5
最初の段落は、誤解を招くものとまっすぐなナンセンスの間のどこかにあります。改行には関連性がありません。2番目の段落は間違った理由で正しいです。lsを解析するのは悪いことですが、rmに魔法のように「パイプ」される可能性があるためではありません。
ジョンクーゲルマンはモニカをサポートします

1
rm標準入力からファイル名を取りますか?私はそうは思いません。また、一般的なルールとして、lsUnixの始まり以来、Unixパイプラインを使用するためのデータソースの主要な例の1つでした。出力が端末である場合の通常のデフォルトの書式設定とは異なり、出力がパイプである場合、属性または装飾のない単純な1行1ファイル名にデフォルト設定されるのはこのためです。
-davidbak

2
@DewiMorganこのウェブサイトは主に非技術的な対象者を対象としているため、ここで悪い習慣を広めたり奨励したりすることは害をもたらし、何も良いことはしません。unix.SEまたは他の技術コミュニティでは、ユーザーは自分で足を撃たずに足に非常に近い位置に照準を合わせる知識/手段を持っているので、(他の慣習に関して)論点は当てはまるかもしれませんが、ここではコメントがスマートに見えません。
イグニス

4

パイプをfile使用してフィードする場合-fは、通常ファイル名が続くオプションを使用しますが、1つのハイフン-を使用してstdinから読み取ることもできます。

$ ls
cow.pdf  some.txt
$ ls | file -f -
cow.pdf:       PDF document, version 1.4
some.txt:        ASCII text

ハイフンを使用したトリックは、-多くの標準のコマンドラインユーティリティで機能します(ただし、場合によっては機能し--ます)ので、常に試してみる価値があります。

このツールxargははるかに強力であり、ほとんどの場合、引数リストが長すぎる場合にのみ必要です(詳細についてはこの投稿を参照してください)。


いつ--ですか?私はそれを見たことがありません。--通常、「フラグの終わり」インジケータです。
ジョンクーゲルマンはモニカをサポートします

はい、しかしプログラマーがそのように使用するいくつかのインスタンス(ab)でそれを見つけました。正確に(私がしなければ、コメントを追加します)ここで、私は覚えていないことが、私はそれを見つけたとき、私は発声呪いを覚えていて、これらの呪いは間違いNSFW ;-)た
deamentiaemundi

2

以下のようなコマンドを使用して動作します

ls | xargs file

私にとってはうまくいくでしょう


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.