私はコマンドラインについて研究しており、|
(パイプライン)はコマンドからの出力を別のコマンドの入力にリダイレクトすることを意図していることを学びました。では、なぜ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
コマンドの使用にエラーがあったため
基本的な問題は、file
が標準入力ではなくコマンドライン引数としてファイル名を要求することです。 ls | file
と記述すると、ls
の出力がfile
への入力として渡されます。引数としてではなく、入力として。
違いは何ですか?
コマンドライン引数は、cmd arg1 arg2 arg3
のように、コマンドの後にフラグとファイル名を書き込むときです。シェルスクリプトでは、これらの引数は変数$1
、$2
、$3
などとして使用できます。Cでは、char **argv
およびint argc
を介してアクセスしますmain()
への引数。
標準入力のstdinは、データのストリームです。 cat
やwc
などの一部のプログラムは、コマンドライン引数が与えられていないときにstdinから読み取ります。シェルスクリプトでは、read
を使用して1行の入力を取得できます。 Cでは、さまざまなオプションの中でscanf()
またはgetchar()
を使用できます。
file
は通常、stdinから読み取りません。少なくとも1つのファイル名が引数として渡されることを想定しています。引数を渡さなかったため、ls | file
を記述するときに使用法を出力するのはこのためです。
xargs
を使用して、ls | xargs file
のように、stdinを引数に変換できます。それでも、 terdon言及 のように、ls
の解析は悪い考えです。これを行う最も直接的な方法は単純です:
file *
あなたが言うように、file
の入力はfilenamesでなければならないからです。ただし、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
は、各ファイル名の後に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
受け入れられた答えは、パイプコマンドがすぐに機能しない理由を説明し、file *
コマンドを使用すると、シンプルで簡単なソリューションを提供します。
いつか役に立つかもしれない別の代替案を提案したいと思います。トリックは、バックティック(`)
文字を使用しています。バックティックについて詳しく説明します ここ 。つまり、バッククォートで囲まれたコマンドの出力を取得し、それを文字列として残りのコマンドに置き換えます。
したがって、find `ls`
はls
コマンドの出力を取得し、find
コマンドの引数として置き換えます。これは、受け入れられているソリューションよりも長く複雑です。ただし、他の状況ではこのバリエーションが役立つ場合があります。
「|」ということを学びました(パイプライン)は、コマンドから別のコマンドの入力に出力をリダイレクトするを意味します。
出力は「リダイレクト」されませんが、プログラムの出力を受け取って入力として使用しますが、fileは入力を受け取りませんが、 引数としてのファイル名 をテストします。リダイレクションはこれらのファイル名を引数として渡しません piping も、あなたがしていることは後でしません。
できることは、テストするすべてのファイルをリストするファイルがある場合は、--files-from
オプションを使用してファイルからファイル名を読み取ることです。そうでない場合は、ファイルへのパスを引数として渡します。
パイプを介したls
の出力は、各行を区切る0x0aのデータのブロック(つまり、改行文字)であり、file
はこれを1つのパラメーターとして取得します。
一般的なルールとして、決してls
を使用して他のコマンドのデータソースを生成しないでください-いつか..をrm
にパイプすると、問題が発生します!
for i in *; do file "$i" ; done
などのループを使用すると、必要な出力が予測どおりに生成されます。スペースを含むファイル名の場合、引用符があります。
パイプを使用してfile
にフィードする場合は、通常はファイル名が続くオプション-f
を使用しますが、単一のハイフン-
を使用してstdinから読み取ることもできます。
$ ls
cow.pdf some.txt
$ ls | file -f -
cow.pdf: PDF document, version 1.4
some.txt: ASCII text
ハイフン-
を使用したトリックは、多くの標準コマンドラインユーティリティで機能します(ただし、--
である場合もあります)。常に試してみる価値があります。
ツールxarg
はより強力であり、ほとんどの場合、引数リストが長すぎる場合にのみ必要です(詳細については この投稿 を参照してください)。
以下のようなコマンドを使用して動作します
ls | xargs file
私にとってはうまくいくでしょう
これも動作するはずです:
file $(ls)
ここでも説明します: https://unix.stackexchange.com/questions/5778/whats-the-difference-between-stuff-and-stuff