How to check directory is empty?

52,892

Solution 1

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

The above is a POSIX-compatible test - and should be very fast. ls will list all files/dirs in a directory excepting . and .. each one per line and will -quote all non-printable characters (to include \newlines) in the output with a ? question-mark. In this way if grep receives even a single character in input it will return true - else false.

To do it in a POSIX-shell alone:

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"

A POSIX-shell (which has not earlier disabled -filename generation) will set the "$@" positional-parameter array to either the literal strings followed by the set command above, or else to the fields generated by glob operators at the end of each. Whether it does so is dependent upon whether the globs actually match anything. In some shells you can instruct a non-resolving glob to expand to null - or nothing at all. This can sometimes be beneficial, but it is not portable and often comes with additional problems - such as having to set special shell-options and afterwards unset them.

The only portable means of handling null-valued arguments involve either empty or unset variables or ~ tilde-expansions. And the latter, by the way, is far safer than the former.

Above the shell only tests any of the files for -existence if neither of the three globs specified resolves to more than a single a match. So the for loop is only ever run at all for three or fewer iterations, and only in the case of an empty directory, or in the case that one or more of the patterns resolves only to a single file. The for also breaks if any of the globs represent an actual file - and as I have arranged the globs in the order of most likely to least likely, it should pretty much quit on the first iteration every time.

Either way you do it should involve only a single system stat() call - the shell and ls should both only need to stat() the directory queried and list out the files its dentries report that it contains. This is contrasted by the behavior of find which would instead stat() every file you might list with it.

Solution 2

With GNU or modern BSDs find, you can do:

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

(assumes $dir doesn't look like a find predicate such as !, (, -name...).

POSIXly:

if [ -d "$dir" ] && files=$(ls -qAH -- "$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

That one checks that $dir is a directory after symlink resolution.

Solution 3

[-z $dir ] complains that there's no command called [-z on most systems. You need spaces around the brackets.

[ -z $dir ] happens to be true if dir is empty, and is false for most other values of dir, but it is unreliable, for example it is true if the value of dir is = -z or -o -o -n -n. Always use double quotes around command substitutions (this goes for the rest of your script as well).

[ -z "$dir" ] tests whether the value of the variable dir is empty. The value of the variable is a string, which happens to be the path to the directory. That doesn't tell you anything about the directory itself.

There's no operator to test whether a directory is empty, like there is for a regular file ([ -s "$dir" ] is true for a directory even if it's empty). A simple way of testing whether a directory is empty is to list its content; if you get empty text, the directory is empty.

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

On older systems that don't have ls -A, you can use ls -a, but then . and .. are listed.


if [ -z "$(LC_ALL=C ls -a -- "$dir")" = "$(printf '.\n..')" ]; then
...
fi

Solution 4

You're looking at the attributes of the directory itself.

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

You want to count how many files are in there:

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

So, the test for "directory is empty" is:

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

Like this:

$ 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

Solution 5

Here's another simple way of doing it. Assume that D is the full path name of the directory you want to test for emptiness.

Then

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

This works because

du -s D

has as output

size_of_D   D

awk removes the D and if the size is 0 the directory is empty.

Share:
52,892

Related videos on Youtube

buddha sreekanth
Author by

buddha sreekanth

Updated on September 18, 2022

Comments

  • buddha sreekanth
    buddha sreekanth almost 2 years

    I have a requirement, if I execute a script ./123 with an arguments of empty path, say /usr/share/linux-headers-3.16.0-34-generic/.tmp_versions(this directory is empty). It should display "directory is empty"

    My code is:

    #!/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
    

    I have an error displays like, if I execute the script name ./123 /usr/src/linux-headers-3.16.0-34-generic/.tmp_versions (this directory is empty).

    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
    

    instead of showing output only "directory is empty" its shows the above output

    The below output has to be display if I exceute the script with correct arguments( I mean with correct directory path). say ./123 /usr/share

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

    my expected output is: ./123 /usr/src/linux-headers-3.16.0-34-generic/.tmp_versions

    directory is empty.
    
  • Otheus
    Otheus about 9 years
    Can confirm. I use mikeserv's initial example in scripts I've written.
  • buddha sreekanth
    buddha sreekanth about 9 years
    could you please explain this part "$(ls -A -- "$dir")". what does $ in and outside of brackets do?
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' about 9 years
    @buddhasreekanth $(…) is command substitution
  • buddha sreekanth
    buddha sreekanth about 9 years
    @Giles "$(ls -A -- "$dir")" is not working its throwing error.
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' about 9 years
    @buddhasreekanth What's the error? Copy-paste. You can't expect people to help you if you just say “not working” without explaining exactly what you observe.
  • buddha sreekanth
    buddha sreekanth about 9 years
    @Giles this is the error. When I executed my script ./filestats its throwing error.. $ /filestats /home/sreekanth/testdir/aki/schatz minimum file size: 4096 /home/sreekanth/testdir/aki/schatz maximum file size: 4096 /home/sreekanth/testdir/aki/schatz average file size: 4 directory is empty. Instead of showing "directory is empty". Its displaying with minimum size, max size and avg size.
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' about 9 years
    @buddhasreekanth I don't see an error here that could result from any of the code snippets I wrote. The code you wrote in the question displays that whether $dir is a directory or not, so this output seems to be what you requested.
  • Stéphane Chazelas
    Stéphane Chazelas about 9 years
    That will report the directory is empty if you don't have read access to it or it does not exist.
  • Stéphane Chazelas
    Stéphane Chazelas about 9 years
    That would report as empty directories that don't exist or for which you don't have read access. For the second one, if you have read access but not search access, YMMV.
  • mikeserv
    mikeserv about 9 years
    @StéphaneChazelas - perhaps, but such reports will be accompanied by error messages to notify the user of such.
  • Stéphane Chazelas
    Stéphane Chazelas about 9 years
    Only in the ls case. globs and [ are silent when they don't have access. For instance, [ -e /var/spool/cron/crontabs/stephane ] will silently report false, while there is a file by that name.
  • Stéphane Chazelas
    Stéphane Chazelas about 9 years
    Also note that those solutions are not equivalent if somedir is a symlink to a directory (or not a directory).
  • terdon
    terdon over 8 years
    Hi and welcome to the site. Please don't post answers that are just code and no explanation. Comment the code or otherwise explain what it is you are doing. By the way, there's no reason to use echo, all you need is list="$somedir/*". Also, this is tricky and prone to errors. You haven't mentioned that the OP should give the target dir's path including the trailing slash, for example.
  • mikeserv
    mikeserv over 5 years
    @StéphaneChazelas could the ´-H´ ´ls´ switch bring them to an approaching equivalence?
  • muru
    muru over 4 years
    $(set -f; echo "$1"/*) seems a rather complicated way of writing "$1/*". And this won't match hidden directories or files.
  • muru
    muru over 4 years
    What I meant is that there is no filename expansion in "$1/*" (note the quotes include *), so the set -f and subshell are unnecessary for that in particular.
  • Dominic108
    Dominic108 over 4 years
    It answers a different question: whether the directory contains non hidden files or directories. I am sorry about that. I will be happy to delete it.
  • muru
    muru over 4 years
    There are ways to match hidden files too (see the complicated glob in mikeserv's answer: ./* ./.[!.]* ./..?*). Maybe you can incorporate that to make the function complete.
  • Dominic108
    Dominic108 over 4 years
    Ah ! I will look at this and learn and maybe we will have a complete answer based on globbing expansion !
  • Stéphane Chazelas
    Stéphane Chazelas about 3 years
    @Freedo, is it possible your $dir is only made of non-characters? Is it better after my latest edit?
  • Freedo
    Freedo about 3 years
    It was made of filenames with numbers only, but yes it seems to work now
  • they
    they over 2 years
    The number of hard links in an empty directory is dependent on the filesystem.
  • waltinator
    waltinator over 2 years
    @they Please provide examples of Linux/Unix filesystems that have other than 2 links in an empty directory, otherwise I'll suspect you're misunderstanding/making it up.
  • they
    they over 2 years
    btrfs. I'm basing that on this answer.
  • Alex Cohn
    Alex Cohn over 2 years
    I have a non-empty directory, and it only contains two hard links. Apparently, the real files are not considered 'hard links'.
  • Admin
    Admin about 2 years
    This test fails for a directory containing an empty directory -- this may still meet the definition of an 'empty directory' for your application, but its something to bear in mind.
  • Admin
    Admin about 2 years
    the way you format your if statements is... sensible. It's like there's finally a reason for bash to have a then statement.