単一(特定)ファイルを持つすべての空のディレクトリとディレクトリを検索する

単一(特定)ファイルを持つすべての空のディレクトリとディレクトリを検索する

すべての空のディレクトリと.DS_StoreMac生成ファイルのみを含むディレクトリを削除するシェルスクリプトを作成しようとしています。電子を簡単にできる

find -depth -type d -empty

しかし、次のようなディレクトリを見つける方法がわかりません。ただ .DS_Store

独自の再帰検索機能を作成せずにこれを行う簡単な方法はありますか?

答え1

POSIX sh+を探す

これはPOSIX findとPOSIX shにのみ依存するソリューションです。すべてのディレクトリを一覧表示し、名前付きエントリのみを含むディレクトリをフィルタリングします.DS_Store

find . -type d -exec sh -c '
    cd "$0" &&
    for x in * .[!.]* ..?*; do
      if [ "$x" = ".DS_Store" ]; then continue; fi;
      if [ -e "$x" ] || [ -L "$x" ]; then exit 1; fi;
    done' {} \; -print
  • 私はfindすべてのディレクトリを再帰的に列挙するために使用します。
  • sh各ディレクトリでいくつかのシェルコードを実行する呼び出しを実行します。
  • このforループはディレクトリ内のすべてのファイルを列挙します。
  • ループ本文をスキップします.DS_Store
  • これら 3 つのパターンのそれぞれがどのファイルにも一致しない場合は、変更されていません。[ -e "$x" ] || [ -L "$x" ]壊れたシンボリックリンクを含むすべてのファイルをキャプチャします。一致しない唯一の理由は、パターンが同じままであるためです。
  • exit 1したがって、ファイル以外のファイルがある場合はシェルフラグメントが実行され.DS_Store、それ以外の場合は成功するとゼロが返されます。
  • 名前印刷以外の操作を実行するには、-print次のように変更します。-exec …

ジッシュ

zshの解決策は次のとおりです。echo実行したいコマンドに変更します。

setopt extended_glob
echo **/*(/DNe\''a=($REPLY/^.DS_Store(DNY1)); ((!#a))'\')
  • **/*すべてのファイルを再帰的に列挙します。
  • とともにグローバル予選 /**/*(/)すべてのディレクトリを再帰的に列挙します。
  • Nglob修飾子は、一致するものがない場合は空のリストを取得することを保証します(zshはデフォルトでエラー信号を送信します)。
  • glob修飾子はDドットファイルを含みます。
  • glob修飾子は、一致する各ファイル名に対して機能し、一致を正常なファイル名に制限します。変数を使用してファイル名を参照できます。e\''CODE'\'CODECODECODE$REPLY
  • ^.DS_Store呼び出されていないファイルと一致します.DS_Store
  • それだけです。パスワード.DS_Storeゼロ以外のファイル数に一致する項目に一致を制限します。
  • glob修飾子はY1一致数を1つに制限します(これは効率の向上にすぎません)。

Python

これはPythonのソリューションです(2と3の両方で動作します)。一列に圧縮されているにもかかわらず、構造は非常に明確です。

python -c 'import os; print("\n".join([path for path, dirs, files in os.walk(".") if dirs == [] and files in ([], [".DS_Store"])]))'
  • os.walkその引数の下にディレクトリのリストを繰り返し返します。各ディレクトリにpath(ディレクトリパス)、dirs(サブディレクトリリスト)、およびfiles(ディレクトリ自体がディレクトリではないディレクトリのファイルリスト)を含むトリプルを作成します。
  • [… for … in os.walk(…) if …]os.walkフィルタリングされた結果
  • ifこの句は、サブディレクトリがなく、外部にファイルがない場合にのみ要素を保存します.DS_Store
  • スクリプトは、中間行と最後の改行の間に改行を含む許可された要素を印刷します。

答え2

簡単な回避策:まず、そのファイルをすべて削除します。

find <path> -type f -name "*.DS_Store" -delete

その後、空のディレクトリを削除します。

コメントに基づいて更新: これらのファイルのみを含むディレクトリのみを削除するには、次のものが必要です(警告:まったくテストされていないため、より多くの参加が必要な場合でも驚かないことがあります)。

find <path> -type d | while read dir; do
    if ! ls --ignore=*.DS_Store $dir; then
        rm -rf $dir
    fi
done

複雑な部分の説明:

ls --ignore=*.DS_Store $dir

.DS_Storeで終わらない$dirのファイルは印刷する必要があります。それ以外の場合、式はFalseであり、ifブロックが実行されます。

答え3

Macの場合:

find . -type d | while read dir; do
    
    # Found empty dir if it only has:
    #   ., .. 
    #   ., .., .DS_Store
    
    if [ "$(ls -am "$dir" | sed -E 's/^., ..$|^., .., .DS_Store$//')" == "" ]; then
        echo rm -R "\"$dir\""
    fi
done

答え4

より多くの保護を備えた別のPOSIXバリアント:

find . -type d -exec sh -c '
  for dir do
    contents=$(ls -AFq "$dir") &&
      case $contents in
        ("" | .[dD][sS]_[sS][tT][oO][rR][eE]) printf "%s\n" "$dir"
      esac
  done' sh {} +

ここで:

  • lsディレクトリの内容を一覧表示できない場合、またはディレクトリの内容が見つからない場合は、問題を解決できます。
  • を使用することでls -F.DS_Store定期的なファイル(-F他の種類のファイルにサフィックスを追加)
  • たとえば、を使用すると、-qファイル名の誤検出を防ぐことができます。$'.DS_Store\n\n'
  • Macosでは、ファイル名は大文字と小文字を区別しないと考えているため、大文字と小文字を区別せずに一致させます。

これに対応する内容はzsh次のとおりです。

empty() {
  set -o localoptions -o extendedglob
  ERRNO=0
  () ((!ERRNO && !$#)) $REPLY/(#i){^.ds_store(NDY1),.ds_store(N^.)}
}
print -rC1 -- **/*(ND/+empty)

これらのディレクトリを削除したい場合は、そのディレクトリdirAにファイルのみが含まれていて、dirBdirA/dirBであるか、.DS_Storeファイルのみが含まれている可能性があります。その後、削除後に空になってもdirA報告しません。dirA/dirB

この問題を解決するにはここを使用できますが、空のディレクトリは見つかるとすぐに削除されるため-depth使用できません。これは重要なので、以下を使用してください。-exec ... {} +-exec ... {} ';'

find . -depth -type d -exec sh -c '
  contents=$(ls -AFq "$1") &&
    case $contents in
      ("" | .[dD][sS]_[sS][tT][oO][rR][eE]) exec rm -rf "$1"
    esac' sh {} ';'

関連情報