a
サブフォルダ内のすべてのファイルを除いて、特定の期間にフォルダにアクセスしなかったすべてのファイルを繰り返し削除したいと思いますb
。
find a \( -name b -prune \) -o -type f -delete
ただし、エラーメッセージが表示されます。
find:-delete操作は自動的に-deepを有効にしますが、-pruneは-deepが適用されている間は何もしません。続行するには、-lengthオプションを明示的に使用してください。
-depth
追加すると、すべてのファイルが含まれます。b
いいえ発生する。
これを行う安全な方法を知っている人はいますか?
答え1
1つの方法は-exec rm
代わりに使用することです-delete
。
find a \( -name b -prune \) -o -type f -exec rm {} +
または-not -path
代わりに使用してください-prune
:
find a -not -path "*/b*" -type f -delete
なぜ-prune
以下と競合するのかを説明してください-delete
。
ヒントを与えて作成することはうまくいかないので、withを使用しようとすると-delete
苦情がわかります。-prune
-delete
-depth
-depth
-prune
以下を使用または使用せずに find 動作を観察します-depth
。
$ find foo/
foo/
foo/f1
foo/bar
foo/bar/b2
foo/bar/b1
foo/f2
単一のディレクトリ内の順序は保証されません。ただし、ディレクトリはコンテンツの前に処理されます。foo/
beforefoo/*
とfoo/bar
beforeを参照してくださいfoo/bar/*
。
これは元に戻すことができます-depth
。
$ find foo/ -depth
foo/f2
foo/bar/b2
foo/bar/b1
foo/bar
foo/f1
foo/
現在がfoo/*
beforeの前に来ていることに注意してくださいfoo/
。と同じですfoo/bar
。
さらなる説明:
-prune
find がディレクトリに降りるのを防ぎます。つまり、-prune
ディレクトリの内容をスキップします。あなたの場合、-name b -prune
これは、 find がその名前のディレクトリに到達すると、すべてのb
サブディレクトリを含むそのディレクトリをスキップすることを意味します。-depth
findにディレクトリ自体の前にディレクトリの内容を処理させます。これは、findがディレクトリエントリの処理を開始したときにb
その内容がすでに処理されていることを意味します。したがって、-prune
無効で-depth
有効です。-delete
つまり、-depth
コンテンツを最初に削除してから空のディレクトリを削除できます。-delete
空でないディレクトリの削除を拒否します。
代替方法の説明:
find a -not -path "*/b*" -type f -delete
覚えやすく、そうでないかもしれません。
コマンドはまだディレクトリに入り、b
その中にあるすべてのファイルを処理しますが、拒否します-not
。ディレクトリが大きいと、b
パフォーマンスの問題が発生する可能性があります。
-path
別の方法で動作します-name
。フルパス-name
ではなく(ファイルまたはディレクトリの)名前のみが一致します。-path
たとえば、パスを観察してみてください/home/lesmana/foo/bar
。-name bar
名前があるので一致しますbar
。-path "*/foo*"
文字列/foo
がパスにあるので一致します。-path
使用する前に理解する必要がある複雑さがいくつかあります。詳しくはマニュアルページをご覧くださいfind
。
これが100%完璧ではないことに注意してください。 「誤検知」の可能性があります。上記のコマンドが作成された方法は、名前がb
(正数)で始まる親ディレクトリを持つすべてのファイルをスキップします。しかし、ツリーの場所に関係なく、名前で始まるすべてのファイルをスキップしますb
(誤検出)。これは、より良い表現を書くことで解決できます"*/b*"
。これは読者に練習問題として残す。
私はあなたがa
andをb
プレースホルダとして使用しており、実際の名前はallosaurus
andに近いと仮定しますbrachiosaurus
。これを置き換えると、誤brachiosaurus
検出b
の数が大幅に減少します。
少なくとも偽の肯定は次のとおりです。いいえあまりにも悲惨でないように削除しました。また、最初にコマンドなしでコマンドを実行し-delete
(暗黙的なコマンドを入力する必要があります-depth
)、出力を調べて誤検出を確認することもできます。
find a -not -path "*/b*" -type f -depth
答え2
rm
代わりに使用してください-delete
:
find a -name b -prune -o -type f -exec rm -f {} +
答え3
上記の答えと説明は非常に役立ちます。
私は「-exec rm {} +」または「-not -path ... -delete」などの回避策を使用しますが、「find ... -delete」よりはるかに遅くなる可能性があります。 NFSファイルシステムのディープディレクトリで、「find ... -delete」が「-exec rm {} +」よりも5倍速く実行されるのを見ました。
「-not path」ソリューションの明らかなオーバーヘッドは、除外されたディレクトリとその下にあるすべてのファイルを表示することです。
"find.. -exec rm{}+" は rm を呼び出してシステムコールを実行します。
fstatat(AT_FDCWD, path...);
unlinkat(AT_FDCWD, path, 0)
「find -delete」はシステムコールを実行します。
fd=open(dir,...);
fchdir(fd);
fstatat(AT_FDCWD, filename,...)
unlinkat(dirfd, filename,...)
したがって、「-exec rm {} +」rmコマンドは各ファイルに対してinodeフルパスルックアップを2回実行しますが、「find -delete」は現在のディレクトリのファイル名を計算してリンクを解除します。ディレクトリから多数のファイルを削除する場合、これは大きな利点です。
(ワインモードオン(申し訳ありません))
-length、-delete、および-pruneの間の対話設計は、「-pruneディレクトリにあるファイル以外のファイルを削除する」という一般的な操作を実行する最も効率的な方法を不必要に削除したようです。
"-type f -delete"の組み合わせはディレクトリの削除を試みないため、-deepなしで実行できる必要があります。または、「find」に「-deletefile」操作がある場合(ディレクトリを削除しないことを意味)、-深さを暗示する必要はありません。
rmにファイル名を並べ替え、ディレクトリを開いて、フルパスリンクを解除する代わりにunlinkat(dir_fd、filename)を実行するオプションがある場合、rmコマンドへのxargsまたはfind -exec呼び出しの速度が速くなる可能性があります。 -rオプションを使用してディレクトリに再帰すると、unlinkat(dir_fd、filename)が実行されました。
(ワインモードオフ)