find コマンドに一致する項目数の制限

find コマンドに一致する項目数の制限

特定の数の一致後にfindコマンドを停止するにはどうすればよいですか?

背景は、フォルダにファイルが多すぎて、ランダムに次のように別のフォルダに配置する必要があることです。

find -max-matches 1000 -exec mv {} /path/to/collection1 \+; 
find -max-matches 1000 -exec mv {} /path/to/collection2 \+; 

これは一人でできますかfind?そうでなければ、最も簡単な方法は何ですか?

答え1

find以下を使用して新しいテストを実装できます-exec

seq 1 1000 |
find . -exec read \; -exec mv {} /path/to/collection1 +

見つかった最初の1000ファイルをに移動します/path/to/collection1

仕組みは次のとおりです。

  • seq 1 10001000行を出力しますfind
  • -exec readラインを読み、パイプが閉じると(seq出力が消費された場合)、失敗します。
  • 前の操作が-exec成功した場合、-exec mv ...移動が実行されます。

-exec ... +期待どおりに動作します。read繰り返しごとに1回実行されますが、find一致するファイルを累積し、mvできるだけ少ない数を呼び出します。

これは次の事実によって異なります。find成功-execまたは失敗は、実行コマンドの終了状態によって異なります。read成功した場合は、上記findの操作を処理し続け(基本演算子は「and」なので)、失敗した場合は停止findします。

findこれをサポートしている場合は、-quitこれを使用して効率を向上させることができます。

seq 1 1000 |
find . \( -exec read \; -o -quit \) -exec mv {} /path/to/collection1 +

これがなければ、find1000 だけを維持してもすべてのファイルがテストされますmv

read外部コマンドとして使用できると仮定して実装しました。POSIX仕様read;そうでない場合は、sh -c readそれを代わりに使用できます。どちらの場合も、findスキャンする各ファイルに対して別々のプロセスが開始されます。

答え2

ディレクトリツリーをナビゲートする以外にはあまり役に立たないので、これをfind行うにはシェルを直接使用することをお勧めします。zsh以下の両方のバリエーションを参照してくださいbash


zshシェルを使う

mv ./**/*(-.D[1,1000]) /path/to/collection1    # move first 1000 files
mv ./**/*(-.D[1,1000]) /path/to/collection2    # move next 1000 files

ワイルドカードパターンは、./**/*(-.D[1,1000])現在ディレクトリ内またはその下にあるすべての一般ファイル(またはそのファイルへのシンボリックリンク)を一致させ、そのうち1000番目のファイルを返します。通常のファイルまたはそのファイルへのシンボリックリンクの一致を制限し、そのように機能-.Dます(隠された名前と一致)。dotglobbash

これは、呼び出し時にワイルドカードパターンの拡張によって生成されたコマンドが大きくなりすぎないと仮定しますmv

上記のアプローチは、各コレクションのグローバルスコープを拡張するので、非効率的です。したがって、パス名を配列に保存してからその中でスライスを移動できます。

pathnames=( ./**/*(-.D) )

mv $pathnames[1,1000]    /path/to/collection1
mv $pathnames[1001,2000] /path/to/collection2

配列を作成するときに配列をランダムに指定するにはpathnames(ランダムファイルを移動したいと言いました):

pathnames=( ./**/*(-.Doe['REPLY=$RANDOM']) )

で同様のことを行うことができます(ただし、結果を提供する場合を除き、 でbashグローバルマッチ結果を簡単に台無しにすることはできないため、その手順をスキップします)。bashshuf

shopt -s globstar dotglob nullglob

pathnames=()
for pathname in ./**/*; do
    [[ -f $pathname ]] && pathnames+=( "$pathname" )
done

mv "${pathnames[@]:0:1000}"    /path/to/collection1
mv "${pathnames[@]:1000:1000}" /path/to/collection2
mv "${pathnames[@]:2000:1000}" /path/to/collection3

答え3

一人ではできないと思いますfind。次のようなものを使用できます。

find [... your parameters ...] -print0 | head -z -1000 | xargs -0 mv -t /path/to/collection

-print0と一緒に使用すると、ファイル名に改行があって-z-0すべてが正しく機能することを確認できます。

答え4

Stephensの答え264963はおそらく私のユースケースに最適です。ただし、この質問のユースケースの簡単な回避策は find と head だけです。

find . [checks] -print -exec ... | head

(少なくともCentOS 8では)以前に評価され、-printパイプが閉じられると、最後までパイプが終了します。-execfindhead

関連情報