ディレクトリが空であることを確認する方法


15

./123たとえば、空のパスの引数を使用してスクリプトを実行すると、/usr/share/linux-headers-3.16.0-34-generic/.tmp_versions(このディレクトリは空です)という要件があります。「ディレクトリが空です」と表示されます

私のコードは:

#!/bin/bash
dir="$1"

if [ $# -ne 1 ]
then
    echo "please pass arguments" 
exit
fi

if [ -e $dir ]
then
printf "minimum file size: %s\n\t%s\n" \
 $(du $dir -hab | sort -n -r | tail -1)

printf "maximum file size: %s\n\t%s\n" \
 $(du $dir -ab | sort -n | tail -1)

printf "average file size: %s"
du $dir -sk | awk '{s+=$1}END{print s/NR}'
else
   echo " directory doesn't exists"
fi

if [ -d "ls -A $dir" ]
 then
    echo " directory is  empty"
fi

スクリプト名を実行すると、./123 /usr/src/linux-headers-3.16.0-34-generic/.tmp_versionsこのようなエラーが表示されます(このディレクトリは空です)。

minimum file size: 4096
    /usr/src/linux-headers-3.16.0-34-generic/.tmp_versions
maximum file size: 4096
    /usr/src/linux-headers-3.16.0-34-generic/.tmp_versions
average file size: 4

「ディレクトリが空です」という出力のみを表示する代わりに、上記の出力を表示します

以下の出力は、正しい引数(つまり、正しいディレクトリパス)を使用してスクリプトを実行した場合に表示される必要があります。いう./123 /usr/share

minimum file size: 196
        /usr/share
    maximum file size: 14096
        /usr/share
    average file size: 4000

私の期待される出力は: ./123 /usr/src/linux-headers-3.16.0-34-generic/.tmp_versions

directory is empty.


誰かが私のコードを一度確認してください。私はそれを編集しました
ブッダ・スリーカンス

回答:


20
if    ls -1qA ./somedir/ | grep -q .
then  ! echo somedir is not empty
else  echo somedir is empty
fi

上記はPOSIX互換のテストであり、非常に高速です。lsディレクトリ除く内のすべてのファイル/ディレクトリの一覧が表示されます.し、..行ごとに各1となります-qすべての非印字可能な文字をuote (含むように\newlines)を使って出力に?疑問符。このようにしてgrep、入力で1文字でも受信した場合はtrueを返し、それ以外の場合はfalseを返します。

POSIXシェルだけでそれを行うには:

cd  ./somedir/ || exit
set ./* ./.[!.]* ./..?*
if   [ -n "$4" ] ||
     for e do 
         [ -L "$e" ] ||
         [ -e "$e" ] && break
     done
then ! echo somedir is not empty
else   echo somedir is empty
fi
cd "$OLDPWD"

POSIXシェル(以前に無効化された-filenameの生成がなかった)set"$@"位置パラメーターの配列を、set上記のコマンドが続くリテラル文字列、またはそれぞれの最後にあるglob演算子によって生成されたフィールドに配置します。そうかどうかは、グロブが実際に一致するかどうかに依存します。一部のシェルでは、解決しないglobにnullに展開するように指示することも、まったくしないように指示することもできます。これは時々有益な場合がありますが、移植性がなく、多くの場合、追加の問題が発生します-特別なシェルオプションを設定し、その後それらの設定を解除する必要があるなどです。

null値の引数を処理する唯一の移植可能な手段には、空の変数または未設定の変数、あるいは~チルダ展開が含まれます。ちなみに、後者は前者よりもはるかに安全です。

上記のシェル-eは、指定された3つのグロブのいずれも1つ以上の一致に解決されない場合にのみ、ファイルの存在をテストします。そのため、forループは3回以下の反復でのみ実行され、ディレクトリが空の場合、または1つ以上のパターンが単一のファイルにのみ解決される場合にのみ実行されます。また、グロブのいずれかが実際のファイルを表す場合forも同様breakです。グロブを最も可能性の高いものから最も可能性の低いものの順に配置したため、毎回最初の反復でほぼ終了するはずです。

どちらの方法でも、1つのシステムstat()コール(シェル)と、照会さlsstat()たディレクトリだけが必要であり、それに含まれるそのdentriesが報告するファイルをリストする必要があります。これは、リストするすべてのファイルのfind代わりに、その動作と対照的stat()です。


確認できます。私が書いたスクリプトでは、mikeservの最初の例を使用しています。
Otheus

これは、存在しない、または読み取りアクセス権がない空のディレクトリとして報告されます。2つ目は、読み取りアクセスはあるが検索アクセスはない場合、YMMV。
ステファンChazelas

@StéphaneChazelas-おそらく、そのようなレポートには、ユーザーにそのことを通知するエラーメッセージが付随します。
mikeserv '20年

1
ls場合のみ。グロブがあり、アクセスできない[場合はサイレントです。たとえば、[ -e /var/spool/cron/crontabs/stephane ]その名前のファイルがある場合、は警告なしでfalseを報告します。
ステファンChazelas

また、somedirがディレクトリへのシンボリックリンク(またはディレクトリではない)の場合、これらのソリューションは同等ではないことに注意してください。
ステファンChazelas

6

GNUまたは最新のBSDを使用するとfind、次のことができます。

if find -- "$dir" -prune -type d -empty | grep -q .; then
  printf '%s\n' "$dir is an empty directory"
else
  printf >&2 '%s\n' "$dir is not empty, or is not a directory" \
                    "or is not readable or searchable in which case" \
                    "you should have seen an error message from find above."
fi

(前提として$dir見えないfind(述語!(-name...)。

POSIXly:

if [ -d "$dir" ] && files=$(ls -qAL -- "$dir") && [ -z "$files" ]; then
  printf '%s\n' "$dir is an empty directory"
else
  printf >&2 '%s\n' "$dir is not empty, or is not a directory" \
                    "or is not readable or searchable in which case" \
                    "you should have seen an error message from ls above."
fi

4

[-z $dir ][-zほとんどのシステムで呼び出されるコマンドがないと不平を言います。括弧の前後にスペースが必要です

[ -z $dir ]dirは空の場合はtrueになり、の他のほとんどの値はfalseになりますがdir、信頼性は低くなります。たとえば、の値dir= -zやの場合はtrueになります-o -o -n -nコマンド置換の前後には常に二重引用符を使用してください(これは、スクリプトの残りの部分にも当てはまります)。

[ -z "$dir" ]変数の値dirが空かどうかをテストします。変数の値は文字列で、たまたまディレクトリへのパスです。ディレクトリ自体については何もわかりません。

通常のファイルの場合のように、ディレクトリが空であるかどうかをテストする演算子はありません([ -s "$dir" ]空の場合でもディレクトリに当てはまります)。ディレクトリが空かどうかをテストする簡単な方法は、その内容をリストすることです。空のテキストを取得した場合、ディレクトリは空です。

if [ -z "$(ls -A -- "$dir")" ]; then 

持っていない古いシステムでls -Aは、使用することができますls -aが、その後...記載されています。

if [ -z "$(LC_ALL=C ls -a -- "$dir")" = ".
.." ]; then 

..文字列には改行と余分なスペースではなく改行のみを含める必要があるため、で始まる行をインデントしないでください。)


この部分「$(ls -A-"$ dir")」について説明してください。括弧の内側と外側の$は何をしますか?
ブッダスリーカンス

@buddhasreekanthは、$(…)あるコマンド置換
ジル「SO-停止が悪さ」

@Giles "$(ls -A-" $ dir ")"がスローエラーを処理していません。
ブッダスリーカント2015年

@buddhasreekanthエラーは何ですか?コピーペースト。あなたが観察したことを正確に説明せずに「機能しない」とだけ言っても、人々があなたを助けることを期待することはできません。
Gilles「SO-邪悪なことをやめなさい」

@Gilesこれはエラーです。スクリプト./filestatsを実行すると、スローエラーが発生します。$ / filestats / home / sreekanth / testdir / aki / schatz最小ファイルサイズ:4096 / home / sreekanth / testdir / aki / schatz最大ファイルサイズ:4096 / home / sreekanth / testdir / aki / schatz平均ファイルサイズ:4つのディレクトリが空です。「ディレクトリが空です」を表示する代わりに。最小サイズ、最大サイズ、平均サイズで表示されます。
ブッダスリーカント2015年

3

あなたはディレクトリ自体の属性を見ています。

$ mkdir /tmp/foo
$ ls -ld /tmp/foo
drwxr-xr-x 2 jackman jackman 4096 May  8 11:32 /tmp/foo
# ...........................^^^^

そこにあるファイルの数を数えたい:

$ dir=/tmp/foo
$ shopt -s nullglob
$ files=( "$dir"/* "$dir"/.* )
$ echo ${#files[@]}
2
$ printf "%s\n" "${files[@]}"
/tmp/foo/.
/tmp/foo/..

したがって、「ディレクトリが空です」のテストは次のとおりです。

function is_empty {
    local dir="$1"
    shopt -s nullglob
    local files=( "$dir"/* "$dir"/.* )
    [[ ${#files[@]} -eq 2 ]]
}

このような:

$ if is_empty /tmp/foo; then echo "it's empty"; else echo "not empty"; fi
it's empty
$ touch /tmp/foo/afile
$ if is_empty /tmp/foo; then echo "it's empty"; else echo "not empty"; fi
not empty

読み取りアクセス権がない場合や存在しない場合は、ディレクトリが空であると報告されます。
ステファンChazelas

1

私の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 nullglobLHSの非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 *です。


$(set -f; echo "$1"/*)かなり複雑な書き方のよう"$1/*"です。そして、これは隠しディレクトリやファイルとは一致しません。
muru

私は何を意味することにはファイル名展開がないことである"$1/*"(引用符が含ま注意し*て)、set -fおよびサブシェルがために不必要であること、特にインチ
muru

それは別の質問に答えます:ディレクトリが非隠しファイルまたはディレクトリを含むかどうか。そのことについて謝ります。削除させていただきます。
Dominic108

1
(中複雑グロブ見すぎて隠しファイルを一致させる方法がありますmikeservの答えは./* ./.[!.]* ./..?*)。多分それを組み込んで機能を完成させることができます。
muru

ああ!私はこれを見て学び、そしておそらくグロビング拡張に基づいた完全な答えを得るでしょう!
Dominic108

0

これを行う別の簡単な方法を次に示します。Dが空かどうかをテストするディレクトリの完全パス名であるとします。

その後

if [[ $( du -s  D |awk ' {print $1}') = 0 ]]
then
      echo D is empty
fi

これは機能します

du -s D

出力として

size_of_D   D

awkはDを削除し、サイズが0の場合、ディレクトリは空です。


-1
#/bin/sh

somedir=somedir
list="`echo "$somedir"/*`"

test "$list" = "$somedir/*" && echo "$somedir is empty"

# dot prefixed entries would be skipped though, you have to explicitly check them

2
こんにちは、サイトへようこそ。コードのみで説明のない回答は投稿しないでください。コードにコメントを付けるか、そうでなければコードの内容を説明します。ところで、使用する理由はありませんecho、あなたが必要とするすべてですlist="$somedir/*"。また、これはトリッキーでエラーが発生しやすい傾向があります。たとえば、OPが末尾のスラッシュを含むターゲットディレクトリのパスを提供する必要があることについては言及していません。
terdon
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.