私のtldrの答えは:
function emptydir {
[ "$1/"* "" = "" ] 2> /dev/null &&
[ "$1/"..?* "" = "" ] 2> /dev/null &&
[ "$1/".[^.]* "" = "" ] 2> /dev/null ||
[ "$1/"* = "$1/*" ] 2> /dev/null && [ ! -e "$1/*" ] &&
[ "$1/".[^.]* = "$1/.[^.]*" ] 2> /dev/null && [ ! -e "$1/.[^.]*" ] &&
[ "$1/"..?* = "$1/..?*" ] 2> /dev/null && [ ! -e "$1/..?*" ]
}
これはPOSIXに準拠しており、重要ではありませんが、ディレクトリを一覧表示して出力をgrepにパイプするソリューションよりも通常は高速です。
使用法:
if emptydir adir
then
echo "nothing found"
else
echo "not empty"
fi
私はhttps://unix.stackexchange.com/a/202276/160204の回答を気に入っています。
function emptydir {
! { ls -1qA "./$1/" | grep -q . ; }
}
ディレクトリをリストし、結果をgrepにパイプします。代わりに、グロブの展開と比較に基づく単純な関数を提案します。
function emptydir {
[ "$(shopt -s nullglob; echo "$1"/{,.[^.],..?}*)" = "" ]
}
この関数は標準のPOSIXではなく、でサブシェルを呼び出します$()
。最初にこの単純な関数を説明して、後で最終的な解決策を理解できるようにします(上記のtldrの回答を参照)。
説明:
左側(LHS)は、展開が発生しない場合は空です。これは、ディレクトリが空の場合です。nullglobオプションは必須です。一致しない場合、glob自体が展開の結果であるためです。(ディレクトリが空の場合、RHSがLHSのグロブと一致することは機能しません。LHSグロブがグロブ自体として名前が付けられた単一のファイルと一致するときに誤検出が発生するためです。*
グロブ*
内のはファイル名のサブストリングと一致します。 )ブレース式{,.[^.],..?}
は隠しファイルをカバーしますが、..
or はカバーしません .
。
shopt -s nullglob
は$()
(サブシェル)の内部で実行されるためnullglob
、現在のシェルのオプションは変更されません。これは通常は良いことです。一方、このオプションはスクリプトで設定することをお勧めします。一致がない場合、グロブが何かを返す傾向があるためです。したがって、スクリプトの最初にnullglobオプションを設定することができ、関数では必要ありません。これを覚えておきましょう:nullglobオプションで動作するソリューションが必要です。
警告:
ディレクトリへの読み取りアクセス権がない場合、関数は空のディレクトリが存在する場合と同じように報告します。これは、ディレクトリを一覧表示して出力をgrepする関数にも適用されます。
shopt -s nullglob
コマンドは、標準のPOSIXではありません。
によって作成されたサブシェルを使用し $()
ます。大したことではありませんが、回避できればいいですね。
プロ:
本当に重要なことではありませんが、この関数は前の関数より4倍高速であり、プロセス内のカーネルで費やされたCPU時間で測定されます。
その他の解決策:
shopt -s nullglob
LHSの非POSIX コマンドを削除し、文字列"$1/* $1/.[^.]* $1/..?*"
をRHSに配置して、という名前のファイルのみ'*'
、.[^.]*
または..?*
ディレクトリにある場合に発生する誤検知を個別に排除できます。
function emptydir {
[ "$(echo "$1"/{,.[^.],..?}*)" = "$1/* $1/.[^.]* $1/..?*" ] &&
[ ! -e "$1/*" ] && [ ! -e "$1/.[^.]*" ] && [ ! -e "$1/..?*" ]
}
shopt -s nullglob
コマンドがなければ、サブシェルを削除することは理にかなっていますが、単語の分割を避け、LHSでのglob拡張を許可するため、注意する必要があります。特に、単語の分割を回避するための引用は機能しません。グロブの展開も妨げられるためです。私たちの解決策は、グロブを個別に考慮することです:
function emptydir {
[ "$1/"* = "$1/*" ] 2> /dev/null && [ ! -e "$1/*" ] &&
[ "$1/".[^.]* = "$1/.[^.]*" ] 2> /dev/null && [ ! -e "$1/.[^.]*" ] &&
[ "$1/"..?* = "$1/..?*" ] 2> /dev/null && [ ! -e "$1/..?*" ]
}
個々のグロブの単語分割はまだありますが、ディレクトリが空でない場合にのみエラーが発生するため、問題ありません。2> / dev / nullを追加して、LHS上の指定されたグロブに一致するファイルが多数ある場合にエラーメッセージを破棄します。
nullglobオプションでも機能するソリューションが必要であることを思い出します。上記のソリューションはnullglobオプションで失敗します。ディレクトリが空の場合、LHSも空になるためです。幸いなことに、ディレクトリが空でないときに空であるとは表示されません。それが空であるときそれが空であると言うことを失敗するだけです。したがって、nullglobオプションを個別に管理できます。ケース[ "$1/"* = "" ]
などを単純に追加することはできません。これらはとして展開されるため[ = "" ]
、構文的に正しくありません。そのため、[ "$1/"* "" = "" ]
代わりに等を使用します 。私たちは、再び3例を考慮しなければならない*
、..?*
と.[^.]*
隠しファイルと一致するように、ではない.
と..
。nullglobオプションがない場合でもこれらは干渉しません。空でない場合でも空であるとは決して言わないためです。したがって、最終的に提案されるソリューションは次のとおりです。
function emptydir {
[ "$1/"* "" = "" ] 2> /dev/null &&
[ "$1/"..?* "" = "" ] 2> /dev/null &&
[ "$1/".[^.]* "" = "" ] 2> /dev/null ||
[ "$1/"* = "$1/*" ] 2> /dev/null && [ ! -e "$1/*" ] &&
[ "$1/".[^.]* = "$1/.[^.]*" ] 2> /dev/null && [ ! -e "$1/.[^.]*" ] &&
[ "$1/"..?* = "$1/..?*" ] 2> /dev/null && [ ! -e "$1/..?*" ]
}
セキュリティ上の懸念:
空のディレクトリに2つのファイルrm
を作成し、プロンプトでx
実行*
します。グロブ*
が展開されrm x
、これが削除のために実行されx
ます。これはセキュリティ上の問題ではありません。私たちの関数では、グロブは展開がコマンドとしてではなく、引数として見られる場所に配置されているためfor f in *
です。