すべての空のディレクトリと.DS_Store
Mac生成ファイルのみを含むディレクトリを削除するシェルスクリプトを作成しようとしています。電子を簡単にできる
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))'\')
**/*
すべてのファイルを再帰的に列挙します。- とともにグローバル予選
/
、**/*(/)
すべてのディレクトリを再帰的に列挙します。 N
glob修飾子は、一致するものがない場合は空のリストを取得することを保証します(zshはデフォルトでエラー信号を送信します)。- glob修飾子は
D
ドットファイルを含みます。 - glob修飾子は、一致する各ファイル名に対して機能し、一致を正常なファイル名に制限します。変数を使用してファイル名を参照できます。
e\''CODE'\'
CODE
CODE
CODE
$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
にファイルのみが含まれていて、dirB
空dirA/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 {} ';'