web-dev-qa-db-ja.com

「xargs」と「file」の奇妙な「No such file」エラー

現在のディレクトリにあるすべてのファイルのMIMEタイプを取得したいのですが。なぜこれが機能しないのですか? OSXまたはLinuxでbashをテストしました。 "file"はファイルが見つからないと不平を言いますが、まったく同じパスで実行すると機能します。

$ find . -type f | xargs -n 1 -I FILE echo $(file --mime-type -b FILE)
ERROR: cannot open './foo.txt' (No such file or directory)
...
$ file --mime-type -b ./foo.txt
text/plain

もちろん、現実の世界では、応答を「エコー」するだけでなく、別のコマンドでMIMEタイプの文字列を使用する必要があります。

これは私が問題を作ることができる最も簡単なものです。私はxargsファイル名置換について何かを理解していないと思いますか?

6
jamshid

コマンド置換$(file --mime-type -b FILE)は、xargsに渡される前にシェルによって実行されるため、必要なものが表示されません。 $(file --mime-type -b FILE)を引用し、xargsを介してbash -cに渡します

find . -type f | xargs -n 1 -I FILE bash -c  'echo $(file --mime-type -b FILE)'
5
iruvar

ここでxargsを使用する必要はありません。 findの_-exec_スイッチを使用するだけです。

_$ find . -type f -exec file --mime-type -- {} +
_

デフォルトfile出力

_$ find 8* -type f -exec file --mime-type -- {} + | tail -5
89999/sample10.txt:               text/plain
89999/sample2.txt:                text/plain
89999/sample4.txt:                text/plain
89999/sample6.txt:                text/plain
89999/sample9.txt:                text/plain
_

_file -b_出力

_$ find 8* -type f -exec file --mime-type -b -- {} + | head -5
application/x-empty
text/x-Perl
text/plain
text/plain
text/plain
_

代替案

これは単なる参考情報ですが、mimetypeと呼ばれる別のコマンドがあり、これを使用して_file --mimetype_と同じことを行うこともできます。このコマンドは、Fedoraのこのパッケージの一部である_Perl-File-MimeInfo_であり、同様に機能します。

_$ find 8* -type f -exec mimetype -- {} + | tail -5
89999/sample10.txt:               text/plain
89999/sample2.txt:                text/plain
89999/sample4.txt:                text/plain
89999/sample6.txt:                text/plain
89999/sample9.txt:                text/plain
_

より手の込んだ幹部

「エコー以上のものを実行すること」について問い合わせがあり、xargsを使用しようとしているので、xargs

しかし、findを使用している場合、これは一般的に最良の方法ではありません。代わりにfindを使用し、次にfindの_-exec_スイッチ内でシェルを呼び出して、xargsを実行させるのではなく、ここでより複雑なことを実行できます。これはより複雑な方法で行われます。

_$ find . -type f -exec sh -c '
   cmd1;
   cmd2;
   file --mime-type -b "$@";
   cmd3;
   cmd4;
 ' sh {} \;
_

NOTE:複数の引数を_+_に渡す_file --mime-type ..._ターミネーターを使用して、一度に1つずつ渡す_\;_に切り替えた。

_$ find 8* -type f -exec sh -c '
   echo -n "mime-type: "; 
   file --mime-type -b "$@"
  ' sh {} \; |& tail -10
mime-type: text/plain
mime-type: text/plain
mime-type: text/plain
mime-type: text/plain
mime-type: text/x-shellscript
mime-type: text/plain
mime-type: text/plain
mime-type: text/plain
mime-type: text/plain
mime-type: text/plain
_
4
slm

あなたの問題はシェル拡張についてです。 $()は、引数として渡される前に展開されます。そのまま扱う場合は、一重引用符で囲み、シェルを呼び出します。

また、ファイル名を処理する場合は、print0と0を使用する習慣をつけてください。これらを省略すると、スペース文字を含むファイル名で問題が発生します。

例:

find . -type f -print0 | xargs -0 -n 1 -I FILE bash -c 'echo $(file --mime-type -b FILE)'

あなたの場合、これは次のように簡略化できます:

find . -type f -print0 | xargs -0 -n 1 file --mime-type -b

追加コメント:見つかったすべてのファイル(「xargs -n 1」またはfind + exec)に対してプログラムを呼び出すことは、実際のパフォーマンスのボトルネックになる可能性があります。数千のファイルを含むディレクトリがある場合、数千のプロセスが生成されます。

3
Bgs