web-dev-qa-db-ja.com

コマンド「ls | file」が機能しないのはなぜですか?

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

file入力は、file filename1 filename2などのファイル名の1つです

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コマンドの使用にエラーがあったため

32
IanC

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

違いは何ですか?

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

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

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

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

file *
71
John Kugelman

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

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

$ ls / | grep etc
etc

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

$ ls
file1 file2

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

前にも言ったように、あなたは本当にlsの出力を解析したくありません。 findのようなものが優れています(print0は、各ファイル名の後にnewilneではなく\0を出力し、xargs-0はそのような入力を処理します;これは、改行を含むファイル名でコマンドを動作させるトリックです):

$ 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
18
terdon

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

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

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

6
Schmuddi

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

出力は「リダイレクト」されませんが、プログラムの出力を受け取って入力として使用しますが、fileは入力を受け取りませんが、 引数としてのファイル名 をテストします。リダイレクションはこれらのファイル名を引数として渡しません piping も、あなたがしていることは後でしません。

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

6
Braiam

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

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

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

5
Mark Williams

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

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

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

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

4
deamentiaemundi

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

ls | xargs file

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

2
SuperKrish

これも動作するはずです:

file $(ls)

ここでも説明します: https://unix.stackexchange.com/questions/5778/whats-the-difference-between-stuff-and-stuff

1
matth