How to check directory is empty?
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 -q
uote all non-printable characters (to include \n
ewlines) 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 -f
ilename 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 -e
xistence 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 break
s 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.
Related videos on Youtube
buddha sreekanth
Updated on September 18, 2022Comments
-
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 about 9 yearsCan confirm. I use mikeserv's initial example in scripts I've written.
-
buddha sreekanth about 9 yearscould you please explain this part "$(ls -A -- "$dir")". what does $ in and outside of brackets do?
-
Gilles 'SO- stop being evil' about 9 years@buddhasreekanth
$(…)
is command substitution -
buddha sreekanth about 9 years@Giles "$(ls -A -- "$dir")" is not working its throwing error.
-
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 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' 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 about 9 yearsThat will report the directory is empty if you don't have read access to it or it does not exist.
-
Stéphane Chazelas about 9 yearsThat 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 about 9 years@StéphaneChazelas - perhaps, but such reports will be accompanied by error messages to notify the user of such.
-
Stéphane Chazelas about 9 yearsOnly 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 about 9 yearsAlso note that those solutions are not equivalent if somedir is a symlink to a directory (or not a directory).
-
terdon over 8 yearsHi 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 islist="$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 over 5 years@StéphaneChazelas could the ´-H´ ´ls´ switch bring them to an approaching equivalence?
-
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 over 4 yearsWhat I meant is that there is no filename expansion in
"$1/*"
(note the quotes include*
), so theset -f
and subshell are unnecessary for that in particular. -
Dominic108 over 4 yearsIt 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 over 4 yearsThere 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 over 4 yearsAh ! I will look at this and learn and maybe we will have a complete answer based on globbing expansion !
-
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 about 3 yearsIt was made of filenames with numbers only, but yes it seems to work now
-
they over 2 yearsThe number of hard links in an empty directory is dependent on the filesystem.
-
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 over 2 yearsbtrfs. I'm basing that on this answer.
-
Alex Cohn over 2 yearsI have a non-empty directory, and it only contains two hard links. Apparently, the real files are not considered 'hard links'.
-
Admin about 2 yearsThis 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 about 2 yearsthe way you format your
if
statements is... sensible. It's like there's finally a reason for bash to have athen
statement.