ファイル名にスペースが含まれているときにfindを使用するにはどうすればよいですか?


17

ファイル名を他のプログラムにパイプしたいのですが、名前にスペースが含まれているとそれらはすべて窒息します。

というファイルがあるとします。

foo bar

find正しい名前を返すにはどうすればよいですか?

明らかに私は欲しい:

foo\ bar

または:

"foo bar"

編集:私は通過したくないxargs、私findはファイル名の文字列を別のプログラムに直接パイプできるように、正しくフォーマットされた文字列を取得したい。


5
何に配管しますか?-execフラグを知っていfindますか?潜在的にこのエラーを軽減-execし、他のコマンドにパイプする代わりに行うことで、コマンドをより効率的にすることができます。ちょうど私の$ .02
h3rrmiller

6
@bug:findファイル名を適切にフォーマットします。1行に1つの名前が書き込まれます。(もちろん、ファイル名に改行文字が含まれている場合、これはあいまいです。)問題は、受信側がスペースを取得するときに「チョーク」することです。つまり、意味のある回答が必要な場合は、受信側を教えてください。
リチ

2
「適切にフォーマットされた」と呼ぶものは、実際には「シェルによる消費のためにエスケープされています」。ファイル名の束を読み取ることができるほとんどのユーティリティは、シェルでエスケープされた名前で窒息しますが、実際には(たとえば)findシェルに適した形式でファイル名を出力するオプションを提供することは理にかなっています。ただし、一般的には、-print0GNU find拡張機能は他の多くのシナリオでも問題なく機能します(あまりにも)。いずれにしても、それを使用することを学ぶ必要があります。
トリプリー

2
@バグ:ところで、ls $(command...)リストをフィードしませんstdin。の出力を$(command...)コマンドラインに直接配置します。その場合、cから読み取っているのはシェルであり、現在の値を使用して$IFS、出力を単語分割する方法を決定します。一般に、を使用する方が適切ですxargs。パフォーマンスが低下することはありません。
リチ

2
find -printf '"%p"\n'見つかった各名前の周りに二重引用符を追加しますが、ファイル名の二重引用符を適切に引用しません。ファイル名に二重引用符が埋め込まれていない場合は、問題を無視できます:またはパイプスルーしsed 's/"/&&/g;s/^""/"/;s/""$/"/'ます。ファイル名がシェルによって処理されることになった場合は、おそらく二重引用符ではなく単一引用符を使用する必要があります(そうでない場合sweet$HOMEはのようになりますsheet/home/you)。そして、これは、改行が含まれるファイル名に対してはまだそれほど堅牢ではありません。それらをどのように処理しますか?
トリプリー

回答:


18

POSIXLY:

find . -type f -exec sh -c '
  for f do
    : command "$f"
  done
' sh {} +

findサポート-print0xargsサポート-0

find . -type f -print0 | xargs -0 <command>

-0 オプションは、ファイル名を終了(分離)するためにスペースではなくASCII NUL文字を使用するようにxargsに指示します。

例:

find . -maxdepth 1 -type f -print0 | xargs -0 ls -l

動作しません。実行するls $(find . -maxdepth 1 -type f -print0 | xargs -0)と、ls: cannot access ./foo: No such file or directory ls: cannot access bar: No such file or directory
バグが発生し

1
Gnoucが実際に書いた方法で試しましたか?自分のやり方でそれを行うことを主張する場合は$(..)、二重引用符で囲んでみてください"$(..)"
-evilsoup

3
@バグ:コマンドが間違っています。正確に試してみて、findとのマンページを読んでくださいxargs
-cuonglm

わかりました。次に、直接パイプできるフォーマットされた文字列を取得したいと思います。
バグ

1
@バグ:xargs -0 <あなたのプログラム>を使用してください
-cuonglm

10

使用-print0は1つのオプションですが、すべてのプログラムがヌルバイト区切りのデータストリームの使用をサポートしているわけではないため、Gnoucの答えが述べたように、いくつかxargs-0オプションを使用する必要があります。

代替は、使用することですfind-exec-execdirオプション。次の最初のものはファイル名をsomecommand一度に1つにフィードし、2番目のものはファイルのリストに展開します。

find . -type f -exec somecommand '{}' \;
find . -type f -exec somecommand '{}' +

多くの場合、グロッビングを使用するほうがよいことがわかります。最新のシェル(bash 4 +、zsh、ksh)を使用している場合、globstar**)を使用して再帰的にグロブを取得できます。bashでは、これを設定する必要があります。

shopt -s globstar
somecommand ./**/*.txt ## feeds all *.txt files to somecommand, recursively

shopt -s globstar extglob.bashrcに行があるので、これは常に有効です(また、拡張されたグロブも有効です)。

再帰性が必要ない場合は、明らかに./*.txt代わりに使用して、作業ディレクトリ内のすべての* .txtを使用してください。findいくつかの非常に便利な詳細な検索機能があり、何万ものファイルに必須です(この時点でシェルの最大数の引数に到達します)が、日常の使用にはしばしば不要です。


ちょっと@evilsoup {}はこのスクリプトで何をしますか?
アユスマン

3

個人的には、-execこの種の問題を解決するために検索アクションを使用します。または、必要に応じて、xargs並列実行を可能にします。

ただし、findbashで読み取り可能なファイル名のリストを作成する方法があります。当然ながら、-execand bash、特にprintfコマンドの拡張機能を使用します。

find ... -exec bash -c 'printf "%q " "$@"' printf {} ';'

ただし、シェルエスケープされた単語は正しく出力されますが、引用符やエスケープは解釈されない$(...)ため、$(...)では使用できません。(結果は$(...)、引用符で囲まれていない限り、単語の分割とパス名の拡張の対象になります。)したがって、次のことはあなたが望むことをしません:

ls $(find ... -exec bash -c 'printf "%q " "$@"' printf {} +)

あなたがしなければならないことは:

eval "ls $(find ... -exec bash -c 'printf "%q " "$@"' printf {} +)"

(私は上記の怪物を実際にテストしようとしていないことに注意してください。)

しかし、あなたもそうするかもしれません:

find ... -exec ls {} +

lsシナリオがOPのユースケースを適切にキャプチャしているとは思いませんが、実際に何を達成しようとしているのかが示されていないため、これは推測にすぎません。このソリューションは実際に非常にうまく機能します。私が試したすべての面白いファイル名について、(漠然と)予想される出力を取得しますtouch "$(tr a-z '\001-\026' <<<'the quick brown fox jumped over the lazy dogs')"
-tripleee

@triplee:OPが何をしたいのかわからない。渡す文字列を作成することの唯一の本当の利点は、evalそれをevalまだ渡す必要がないことです。パラメータに保存し、後で異なるコマンドで何度か使用することができます。しかし、OPは、それがユースケースであることを示していません(もしそうなら、ファイル名を配列に入れる方が良いかもしれませんが、それもトリッキーです。)
リチ

0
find ./  | grep " "

スペースを含むファイルとディレクトリを取得します

find ./ -type f  | grep " " 

ファイルにスペースが含まれています

find ./ -type d | grep " "

ディレクトリにスペースが含まれています


-2
    find . -type f -name \*\  | sed -e 's/ /<thisisspace>/g'

これは興味深い回答ですが、この質問に対する答えではありません。
スコット
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.