
特定の数の一致後に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 1000
1000行を出力します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 +
これがなければ、find
1000 だけを維持してもすべてのファイルがテストされます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
ます(隠された名前と一致)。dotglob
bash
これは、呼び出し時にワイルドカードパターンの拡張によって生成されたコマンドが大きくなりすぎないと仮定しますmv
。
上記のアプローチは、各コレクションのグローバルスコープを拡張するので、非効率的です。したがって、パス名を配列に保存してからその中でスライスを移動できます。
pathnames=( ./**/*(-.D) )
mv $pathnames[1,1000] /path/to/collection1
mv $pathnames[1001,2000] /path/to/collection2
配列を作成するときに配列をランダムに指定するにはpathnames
(ランダムファイルを移動したいと言いました):
pathnames=( ./**/*(-.Doe['REPLY=$RANDOM']) )
で同様のことを行うことができます(ただし、結果を提供する場合を除き、 でbash
グローバルマッチ結果を簡単に台無しにすることはできないため、その手順をスキップします)。bash
shuf
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
パイプが閉じられると、最後までパイプが終了します。-exec
find
head