find を使用して特定のディレクトリを見つけ、1 つのディレクトリを除くその中のすべてのファイルを削除します。

find を使用して特定のディレクトリを見つけ、1 つのディレクトリを除くその中のすべてのファイルを削除します。

次のような問題があります。ディレクトリ構造があり、 という名前のディレクトリ内にあるものをすべて削除したいのですCachesが、 という名前のディレクトリ以外はすべて削除する必要がありますSnapshots。 でこれを行う方法がわかりません。探すまたは私が知っている他のコマンド。

私が質問する理由: iOS では、すべてのアプリに独自のキャッシュ ディレクトリがあります。アプリによっては、キャッシュが適切にクリアされないことがあります。この回答の解決策を使用すると、デバイスのドライブが別のコンピューターにマウントされている場合 (たとえば、FUSE (iExplorer) を使用)、iOS 上のキャッシュをクリアしてディスク領域を最適化できるようになります。

これまでのところ、次のものがあります:

find . 2>/dev/null -type d -name "Caches" -maxdepth 3 -print

次のような結果が返されます:

./Library/Caches

を実行すると、ls ./Library/Cachesすべてのコンテンツとディレクトリが表示されますが、最終的にはこれ以外のすべてSnapshotsを表示したいので、除外したいコンテンツとディレクトリが表示されます。-delete


次のようなものがほしいです:

  Before:                            After:

  .                                  .
  ├── a                              ├── a
  │   ├── a                          │   ├── a
  │   └── Caches                     │   └── Caches
  │       ├── a                      │       └── a
  │       │   └── Snapshots          │           └── Snapshots
  │       │       └── a              │               └── a
  │       ├── b                      └── b
  │       │   └── a                      └── c
  │       └── c                  
  └── b
      ├── c
      └── Caches
          ├── a
          │   └── foo
          │       └── a
          └── b
              └── a

答え1

質問の文言に少し混乱しています。./Library/Cachesという 1 つのフォルダを除くディレクトリ内のすべてを削除するだけであればSnapshots、 を使用する必要はありませんfind。 ではbash、シェル グロブが最も簡単な方法です。

shopt -s extglob  # probably already enabled
echo Library/Caches/!(Snapshots)

削除するファイル/ディレクトリがすべて印刷される場合は、echoを に置き換えますrm -f --

保存したいSnapshotsディレクトリ ツリーの異なるレベルに複数のディレクトリがある場合、GNU では次の操作を実行できます。./Library/Cachesfind

find Library/Caches ! -path '*Snapshots*'

これにより、パスにあるものを除くすべてのファイル/ディレクトリが出力されますSnapshots 。ディレクトリを含む (またはその子にディレクトリが含まれる)Snapshotsディレクトリが含まれますが、find ... -deleteそれらは削除されず、空ではないというエラーが出力されます。満足したら、-delete最後に追加します。

1つの注意点は、これによりファイル名前はSnapshotsそのままです。これが問題になる場合は、代わりに次の操作を実行してください。

find Library/Caches \( ! -path '*Snapshots*' -o -type f -name Snapshots \)

満足したらまた追加します-delete

答え2

find . -depth -print0 | perl -0lne '
  if ("$_/" =~ m{/Caches(/.*)}s && $1 !~ m{/Snapshots/}) {rmdir $_ or unlink $_}'

が をfindサポートしていない場合は-print0、 に置き換えることができます-exec printf '%s\0' {} +

アイデアは、ファイルのリストを NUL で終了して印刷し (0 はファイル パスに出現できない唯一のバイトであるため)、perl-nwith-0オプションを使用して、それらのファイル名ごとにコードを実行することです ($_ファイル名に設定)。

を使用すると-depth、ファイルは親ディレクトリの前に印刷されます。depthパスに が含まれ、/Caches/その後に が続かない場合にのみ、ファイルまたはディレクトリを削除します (空であると想定しているため、リストを順番に処理することが重要です) /Snapshosts/

答え3

私は魔法使いではないfindので、 ではできないとは言いたくありませんfindが、 を数行記述すればできることは知っていますbash。ディレクトリ構造を正しく理解していると仮定すると、次のようになります。

#!/bin/bash

for i in Library/Caches/*; do
    if [[ -d "$i" ]]; then
         [[ "$i" =~ "Snapshots" ]] ||  echo "rm-ing  $i" # change to rm -rf "$i" to use
    fi
done

forは の各項目を返しますLibrary/Caches/if [[ -d "$i" ]]文は各項目がディレクトリであるかどうかを確認し、ディレクトリである場合は、その名前に「Snapshots」が含まれているかどうかを確認します[[ "$i" =~ "Snapshots" ]]。 のように正規表現の一致を否定する演算子がないため、前のコマンドが失敗した場合 (ディレクトリ名に Snapshots が含まれていない場合など) は を実行してい!=~ました。||rm

これは を使用する最新のシステムであればどれでも実行できますbashが、 が必要ですbash。また、ディレクトリ構造をいじる必要があるかもしれませんが、これで十分でしょう。

答え4

最初の部分: キャッシュという名前のディレクトリを見つけて、それに対して何かを行う

find . -name Caches -type d -exec … \;

2 番目の部分: キャッシュ ディレクトリを走査し、すべてを削除しますが、Snapshotsサブディレクトリとその内容 (およびそこに至るパス) は保持します。深さ優先走査を実行して、ディレクトリが空になったら削除できるようにします。

    find /path/to/Caches -depth -name Snapshots -type d -prune \
                             -o \( -type d -empty -o ! -type d \) -delete

では、これらを組み合わせてみましょう。

find . -name Caches -type d -exec
    find {} -depth -name Snapshots -type d -prune \
                -o \( -type d -empty -o ! -type d \) -delete

関連情報