247

I want to test if a directory doesn't contain any files. If so, I will skip some processing.

I tried the following:

if [ ./* == "./*" ]; then
    echo "No new file"
    exit 1
fi

That gives the following error:

line 1: [: too many arguments

Is there a solution/alternative?

Anthony Kong
  • 5,366

27 Answers27

321
if [ -z "$( ls -A '/path/to/dir' )" ]; then
   echo "Empty"
else
   echo "Not Empty"
fi

ls -A means list all but not . or ..

Also, it would be best to check if the directory exists before.

  • 14
    I had trouble getting this method to work when the /path/to/dir contained spaces and needed to be quoted. I used [ $(ls -A "$path" | wc -l) -ne 0], inspired by @ztank1013's answer. – pix May 19 '15 at 03:23
  • 2
    For those who are looking for a one liner : [ "$(ls -A ./path/to/dir)" ] && echo 'NOT EMPTY' || echo 'EMPTY' – tdhulster Jun 03 '16 at 16:29
  • 3
    Just in case if someone will be looking for "correct/stable" one liner: [ -z "$(ls -A /path/to/dir)" ] && { echo "Not Empty" ; YourCommandA ; true ; } || { echo "Empty" ; YourCommandB ; }. – Victor Yarema Oct 12 '17 at 23:24
  • 6
    This checks whether the directory exists, and deals with spaces in the path (notice the nested quotes in the subshell):

    if [ -d "/path/to/dir" ] && [ -n "$(ls -A "/path/to/dir")" ]; then echo "Non-empty folder" else echo "Empty or not a folder" fi

    – Jonathan H Jan 10 '19 at 00:40
  • 2
    Honestly, at this point, if it's my own system I just write a quick 10 line Python script, symlink it to /usr/local/bin or something, and then call it like if isNotEmpty "$directory"; then ... fi – bjd2385 Aug 27 '19 at 19:02
  • 2
    doesn't work for me, bash 5.0, ls from coreutils 8.30, it returns directory is not empty even though its empty – mauek unak Mar 08 '20 at 18:46
  • 2
    If you want to check if the directory is NOT empty (i.e. the inverse to the original question) change -z to -n. – Finncent Price Jun 21 '21 at 16:46
  • 2
    Can you explain why -z is used? I saw in this tutorial - https://www.cyberciti.biz/faq/linux-unix-shell-check-if-directory-empty/ - practically the same approach but does not use -z – Manuel Jordan Dec 10 '21 at 23:07
  • 2
    /sdcard/WhatsApp/.trash/ on adb is empty but -z will not detect empty state removing -z will work.[ -z "$(ls -A /sdcard/WhatsApp/.trash/) " ] && echo "there no op. But [ "$(ls -A /sdcard/WhatsApp/.trash/) " ] && echo "there" there` maybe you didnt think of hidden dirs. Update answer accordingly – user1874594 Apr 25 '22 at 02:04
  • 2
    @user1874594 sorry, this is very adb specific. I'd like to keep the answer more generic – Andrey Atapin Apr 27 '22 at 06:50
  • 2
    @ManuelJordan Check the edit history. -z was added later just to make it more explicit. – M Imam Pratama May 04 '22 at 10:14
  • Macos Big Sur, using zsh. Doesn't work, even though man ls stated -A has this intent. A simple fix was to regex out the . and .. via -z $(ls -A $tgt | egrep -v '^\.{1,2}$' ) – JL Peyret Oct 10 '23 at 22:01
  • Unknown argument -A – Turkeyphant Aug 13 '24 at 14:28
  • I don't know how common it is for people to have aliased ls, but I had to use \ls (and didn't realize it at first) to avoid alias expansion if your aliased ls has output even when the directory is empty. Looks like another option would have been to use command ls. – Dan Jackson Feb 07 '25 at 22:25
  • This is not entirely foolproof/secure. In the unlikely but possible case that the files in the directory have filenames consisting solely of newline characters, ls will print them literally since stdout is not a terminal, and they will then be stripped by command substitution before test -z gets a chance to see them. You can fix this in a POSIX-portable way by using ls -Aq instead. – djpohly Apr 30 '26 at 23:27
48

No need for counting anything or shell globs. You can also use read in combination with find. If find's output is empty, you'll return false:

if find /some/dir -mindepth 1 -maxdepth 1 | read; then
   echo "dir not empty"
else
   echo "dir empty"
fi
slhck
  • 237,466
  • Nice solution, but I think your echo calls reflect the wrong result : in my test (under Cygwin) find . -mindepth 1 | read had a 141 error code in a non-empty dir, and 0 in an empty dir – Lucas Cimon Dec 20 '17 at 09:17
  • @LucasCimon Not here (macOS and GNU/Linux). For an non-empty directory, read returns 0, and for an empty one, 1. – slhck Dec 20 '17 at 11:28
  • 13
    PSA does not work with set -o pipefail – Colonel Thirty Two Sep 10 '19 at 02:07
  • For my case empty or not exist is the same. Can add 2>/dev/null before the pipe to avoid showing error when folder does not exist No such file or directory. – Martin P. Mar 14 '24 at 16:55
  • 1
    Maybe this answer can be rewrote with the find -empty flag and without using the read pipe – V. Bozz Dec 02 '24 at 17:26
42
if [ -n "$(find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty 2>/dev/null)" ]; then
    echo "Empty directory"
else
    echo "Not empty or NOT a directory"
fi
uzsolt
  • 1,325
  • 1
    Correct and fast. Nice! – l0b0 Feb 24 '12 at 11:00
  • 5
    It needs quotes (2x) and the test -n to be correct and safe (test with directory with spaces in the name, test it with non-empty directory with name '0 = 1'). ... [ -n "$(find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty 2>/dev/null)" ]; ... – Zrin Mar 07 '17 at 23:51
  • 2
    @ivan_pozdeev That's not true, at least for GNU find. You may be thinking of grep. https://serverfault.com/questions/225798/can-i-make-find-return-non-0-when-no-matching-files-are-found – Vladimir Panteleev Jun 19 '18 at 10:55
  • 5
    It might be simpler to write find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty | grep ., and rely on the exit status from grep. Whichever way you do it, this is very much the right answer to this question. – Tom Anderson Jul 06 '18 at 13:56
  • 2
    I like that this doesn't require listing/reading/traversing all files in the directory. Unlike "ls" based solutions this should work much faster when the directory has lots of files. – oᴉɹǝɥɔ Oct 03 '20 at 18:24
  • 2
    Found this question from a search, discarded poor answers until i found this one, tried it, found it didn't work with GNU find, went to add a comment to that effect ... and found i'd already done that two years ago. Although it should be grep -q .. Thanks again, Mr Udvari! – Tom Anderson Oct 14 '20 at 20:11
  • grep is slower than -n – Alberto Salvia Novella Dec 19 '22 at 18:56
32

With FIND(1) (under Linux and FreeBSD) you can look non-recursively at a directory entry via "-maxdepth 0" and test if it is empty with "-empty". Applied to the question this gives:

if test -n "$(find ./ -maxdepth 0 -empty)" ; then
    echo "No new file"
    exit 1
fi
TimJ
  • 429
  • 3
    It may not be 100% portable, but it's elegant. – Craig Ringer Nov 28 '18 at 02:59
  • 3
    This also finishes early in large directories, works with pipefail: `set -o pipefail;

    { find "$DIR" -mindepth 1 || true ; } | head -n1 | read && echo NOTEMPTY || echo EMPTY `

    – macieksk Nov 24 '19 at 12:29
  • Was hoping to find a flag that made find return non-zero when not finding results, but this is probably the closest we're getting to that. Would at least be nice with an equivalent to ! -empty -exit 1 for GNU though. – Steen Schütt Jun 01 '21 at 10:44
  • 4
    Finally a correct answer. No attempts at parsing ls output, not doing anything extra (like listing all files, then counting them), etc. Excellent answer. – lmat - Reinstate Monica Dec 06 '21 at 16:17
  • This is succinct and works. It should be noted (for future Googlers) that ./ on line 1 can be replaced with whatever directory you're checking. – bobsbeenjamin Sep 20 '24 at 21:57
  • The trailing slash on the directory name is actually critical here if the directory is provided externally (e.g. as a parameter). Command substitution will strip any newline characters at the end of the output of find, so if the provided directory is, for example, $'\n\n\n', you need it to print that slash! I'd recommend skipping command substitution altogether so you don't have to worry about Bash minutiae and instead piping the output to cmp -s /dev/null. – djpohly Apr 30 '26 at 23:36
25
#!/bin/bash
if [ -d /path/to/dir ]; then
    # the directory exists
    [ "$(ls -A /path/to/dir)" ] && echo "Not Empty" || echo "Empty"
else
    # You could check here if /path/to/dir is a file with [ -f /path/to/dir]
fi
Renaud
  • 484
13

What about testing if directory exists and not empty in one if statement

if [[ -d path/to/dir && -n "$(ls -A path/to/dir)" ]]; then 
  echo "directory exists and is not empty"
else
  echo "directory doesn't exist or is empty"
fi
aleb
  • 103
  • 3
stanieviv
  • 139
10

Use the following:

count="$( find /path -mindepth 1 -maxdepth 1 | wc -l )"
if [ $count -eq 0 ] ; then
   echo "No new file"
   exit 1
fi

This way, you're independent of the output format of ls. -mindepth skips the directory itself, -maxdepth prevents recursively defending into subdirectories to speed things up.

Daniel Beck
  • 112,443
9

A hacky, but bash-only, PID-free way:

is_empty() {
    test -e "$1/"* 2>/dev/null
    case $? in
        1)   return 0 ;;
        *)   return 1 ;;
    esac
}

This takes advantage of the fact that test builtin exits with 2 if given more than one argument after -e: First, "$1"/* glob is expanded by bash. This results in one argument per file. So

  • If there are no files, the asterisk in test -e "$1"* does not expand, so Shell falls back to trying file named *, which returns 1.

  • ...except if there actually is one file named exactly *, then the asterisk expands to well, asterisk, which ends up as the same call as above, ie. test -e "dir/*", just this time returns 0. (Thanks @TrueY for pointing this out.)

  • If there is one file, test -e "dir/file" is run, which returns 0.

  • But if there are more files than 1, test -e "dir/file1" "dir/file2" is run, which bash reports it as usage error, i.e. 2.

case wraps the whole logic around so that only the first case, with 1 exit status is reported as success.

Possible problems I haven't checked:

  • There are more files than number of allowed arguments--I guess this could behave similar to case with 2+ files.

  • Or there is actually file with an empty name--I'm not sure it's possible on any sane OS/FS.

Alois Mahdal
  • 2,404
  • 2
    Minor correction: if there is no file in dir/, then test -e dir/* is called. If the only file is '*' in dir then test will return 0. If there are more files, then it returns 2. So it works as described. – TrueY Sep 07 '18 at 11:43
  • You're right, @TrueY, I've incorporated it in the answer. Thanks! – Alois Mahdal Sep 11 '18 at 14:45
  • 2
    This won't work if the directory contains a single file named *. A pathological case, but I've seen worse. – kkm mistrusts SE Aug 28 '21 at 03:02
  • @kkm it did work for me: http://hastebin.com/hevumumoma.txt -- Bash version 5.1.0(1)-release – Alois Mahdal Sep 10 '21 at 11:44
  • OTOH, if it does not work on older Bash (let me know) one could address that case by adding test -e "$1/*" && return 1 as the first line. – Alois Mahdal Sep 10 '21 at 11:47
  • @AloisMahdal, thank you, good to know, thanks! I usually test features like that with docker run --rm -it bash:4.3, for some arbitrary value of 4.3 :) These are tiny Alpine images, fast to pull and not eating much disk space. 'fraid I have no time to test them.

    It has always baffled me how such a common problem does not have a simple canonical solution in shells.

    – kkm mistrusts SE Sep 19 '21 at 14:56
  • @kkm what's your point? have you seen the function fail or not? – Alois Mahdal Sep 20 '21 at 08:56
8

This will do the job in the current working directory (.):

[ `ls -1A . | wc -l` -eq 0 ] && echo "Current dir is empty." || echo "Current dir has files (or hidden files) in it."

or the same command split on three lines just to be more readable:

[ `ls -1A . | wc -l` -eq 0 ] && \
echo "Current dir is empty." || \
echo "Current dir has files (or hidden files) in it."

Just replace ls -1A . | wc -l with ls -1A <target-directory> | wc -l if you need to run it on a different target folder.

Edit: I replaced -1a with -1A (see @Daniel comment)

ztank1013
  • 571
  • 3
    use ls -A instead. Some file systems don't have . and .. symbolic links according to the docs. – Daniel Beck Oct 31 '11 at 10:12
  • 2
    Thanks @Daniel, I edited my answer after your suggestion. I know the "1" might be removed too. – ztank1013 Oct 31 '11 at 10:21
  • 4
    It doesn't hurt, but it's implied if output it not to a terminal. Since you pipe it to another program, it's redundant. – Daniel Beck Oct 31 '11 at 10:24
  • 2
    -1 is definitely redundant. Even if ls will not print one item per line when it will be piped then it doesn't affect the idea of checking if it produced zero or more lines. – Victor Yarema Oct 13 '17 at 09:31
  • Try that on an NFS mount in a directory with 1-2 million files. You'll be pleasantly surprised that a simple empty-or-not check takes 30 min. Sticking head into the pipeline, like (( $( ls -1A | head -n1 | wc -l ) == 0 )) may be a fix, if only partial. – kkm mistrusts SE Aug 28 '21 at 03:09
7

Using an array:

files=( * .* )
if (( ${#files[@]} == 2 )); then
    # contents of files array is (. ..)
    echo dir is empty
fi
glenn jackman
  • 28,014
  • 7
    Very nice solution, but note that it requires shopt -s nullglob – xebeche Jan 16 '17 at 11:24
  • 3
    The ${#files[@]} == 2 assumption doesn't stand for the root dir (you will probably not test if it's empty but some code that doesn't know about that limitation might). – ivan_pozdeev Jan 21 '18 at 04:29
  • 3
    @ivan_pozdeev: What do you mean? When I do cd / && files=(* .*), I get an enumeration of all the files and directories in the root directory, which includes . and ... So the ${#files[@]} == 2 test is valid. – Scott - Слава Україні Feb 03 '20 at 04:08
  • Yes, this is ambiguous when you don't know whether nullglob is set (returns 3 array entries in an empty dir when unset). And as long as you're setting nullglob, you may as well set dotglob as well, as noted in the BashFAQ for this issue, then you can just use files=(*) and test against 0. – SpinUp __ A Davis Jun 14 '22 at 20:00
  • @xebeche Did you mean shopt -u nullglob ? – Daniel T Nov 14 '25 at 23:15
  • @DanielT No: d=$RANDOM && mkdir $d && cd $d && shopt -s nullglob && files=( * .* ) && echo -n "${#files[@]} ${files[*]} vs. "; shopt -u nullglob && files=( * .* ) && echo "${#files[@]} ${files[*]}" should return 2 . .. vs. 3 * . .. – xebeche Nov 16 '25 at 15:43
  • @xebeche The behavior changed on Docker ubuntu:22.10 +. Your command outputs 0 vs. 2 * .* for me – Daniel T Nov 16 '25 at 20:52
3

There are lots of good answers here for simple cases, but this QA ranks very highly in a web search, and many of the answers have subtle failures that may be important for some use cases:

  • permissions problems (i.e. reporting empty dir when it's only unsearchable)
  • ambiguity about symlinks (test dereferences, find doesn't by default, ls may depend on whether the path ends with /)
  • answers using shell globbing may rely on options like nullglob that need to be tested or explicitly set

Here is a bash shell function that is efficient for a directory with lots of files, doesn't rely on shell options for globbing, and explicitly tests for permission problems:

mtdir() {

# Robust test for empty directory
# Usage: mtdir &lt;dir_name&gt;
# Return codes:
#   0 : empty
#   1 : non-empty
#   2 : evaluation not possible 

if [[ -d &quot;$1&quot; &amp;&amp; -r &quot;$1&quot; &amp;&amp; -x &quot;$1&quot; ]]
then
    if find -L -- &quot;$1&quot; -maxdepth 0 -type d -empty | grep -q .
    then
        # empty directory
        return 0
    else
        # non-empty directory
        return 1
    fi
else
    # consider printing an error
    echo &quot;argument $1 is not a directory, not readable, or not searchable by this user&quot;
    return 2
fi

}

The function can be used with:

if mtdir some/dir
then
    echo "directory is empty"
    # further processing
fi

You can also test for a non-empty directory explicitly, or an error, using the return code:

mtdir some/dir
ec=$?

if [[ $ec -eq 1 ]] then echo "non-empty directory" # further processing

elif [[ $ec -eq 2 ]] then echo "unexpected error: not a directory, not readable, or not searchable" # further processing fi

This function has been tested and works under Linux and macOS, with edge cases of non-directory files, symlinks, and directories with locked down permissions (i.e. chmod 0000 testdir).

As written, valid symlinks are dereferenced (followed), testing the targets rather than the links themselves (both at the -d test, and using find -L). If symbolic links should be tested as link files rather than their target for your application, an explicit test should be added before -d (e.g. ! -L "$1" && ...), and find -P should be used instead of find -L.

2

This solution is using only shell built-ins:

function is_empty() {
  typeset dir="${1:?Directory required as argument}"
  set -- ${dir}/*
  [ "${1}" == "${dir}/*" ];
}

is_empty /tmp/emmpty && echo "empty" || echo "not empty"
pez
  • 31
2

I think the best solution is:

files=$(shopt -s nullglob; shopt -s dotglob; echo /MYPATH/*)
[[ "$files" ]] || echo "dir empty" 

thanks to https://stackoverflow.com/a/91558/520567

This is an anonymous edit of my answer that might or might not be helpful to somebody: A slight alteration gives the number of files:

files=$(shopt -s nullglob dotglob; s=(MYPATH/*); echo ${s[*]}) 
echo "MYPATH contains $files files"

This will work correctly even if filenames contains spaces.

akostadinov
  • 1,580
  • 1
    Using an array instead of invoking a subshell will make this even better. This answer does that. – codeforester Mar 06 '20 at 21:17
  • 2
    @codeforester, the solution needs to know what are current shell opts. So one would need code to set opts and revert if needed (or figure out opts and count according to them). This will bloat the code and make it less readable. If this part of the code is on a performance critical path, then it might be worth it (or not, needs testing). For a normal use case where you check one directory, I think this answer is safe, short and self contained. If you don't need portability between scripts (which possibly use different shell opts), then the other answer is good. – akostadinov Mar 08 '20 at 10:10
  • @akostadinov, I think having an agreement on what options are set is a better way to write shell project. Otherwise, yes, it can affect cleanliness, portability, and the need to revert options, but it is a bad design IMHO. Usually you always set nullglob at the beginning of the script and use something like f=("$dir"/{,.}*) – Alek May 31 '24 at 18:58
  • @Alek, this answer is not a guide how to write good shell scripts. This is a safe answer to copy paste. You have experience, you see the necessary options, you can set them as you wish. The novices will likely not shoot themselves in the foot. If I'm to give a recommendation about writing clean shell scripts, that would be not to write shell scripts 8-) – akostadinov May 31 '24 at 21:34
  • @akostadinov, you said This will bloat the code and make it less readable and I just corrected that it is not true, if you are an experienced programmer. To extend your answer for pros or to ignore pro audience is up to you ;) – Alek Jun 01 '24 at 10:01
  • @Alek, more explanation is not ignoring pro audience. I answer the way i like to see answers. Short snippets that are mostly safe to use as they are. I hate long answers where it is hard to understand the idea reading more than a few lines. I mean, explanations are fine as long as there is a tl;dr; There are at least a couple of bash-strict projects that are great for longer scripts. But it will be stupid to explain about them in all answers. Also advising to add them to existing projects may not be useful for many due to incompatible legacy code. It's fine if you to answer in your own style. – akostadinov Jun 03 '24 at 08:37
  • @akostadinov, I don't have any complaints about the answer! Your answer is good :) It's all about the false statements in your first comment, that's it. It's probably not a good word I chosen, but "ignoring" is about that. If this was a beginner only forum or you stated it as a caution for beginners - that would probably be ok :) Sorry if I was unclear – Alek Jun 03 '24 at 09:46
1
if find "${DIR}" -prune ! -empty -exit 1; then
    echo Empty
else
    echo Not Empty
fi

EDIT: I think that this solution works fine with gnu find, after a quick look at the implementation. But this may not work with, for example, netbsd's find. Indeed, that one uses stat(2)'s st_size field. The manual describes it as:

st_size            The size of the file in bytes.  The meaning of the size
                   reported for a directory is file system dependent.
                   Some file systems (e.g. FFS) return the total size used
                   for the directory metadata, possibly including free
                   slots; others (notably ZFS) return the number of
                   entries in the directory.  Some may also return other
                   things or always report zero.

A better solution, also simpler, is:

if find "${DIR}" -mindepth 1 -exit 1; then
    echo Empty
else
    echo Not Empty
fi

Also, the -prune in the 1st solution is useless.

EDIT: no -exit for gnu find.. the solution above is good for NetBSD's find. For GNU find, this should work:

if [ -z "`find \"${DIR}\" -mindepth 1 -exec echo notempty \; -quit`" ]; then
    echo Empty
else
    echo Not Empty
fi
yarl
  • 208
1

The Question was:

if [ ./* == "./*" ]; then
    echo "No new file"
    exit 1
fi

Answer is:

if ls -1qA . | grep -q .
    then ! exit 1
    else : # Dir is empty
fi
HarriL
  • 19
1
[ $(ls -A "$path" 2> /dev/null | wc -l) -eq 0 ] && echo "Is empty or not exists." || echo "Not is empty."
1

Although there are many reasonable solutions here, I am personally not a big fan of most of these answers, as many return a lot of output when returning directory contents. I was expecting a solution that would better handle directories with large numbers of files, while also one that I think is easy to understand.

So, this is what I ended up with, and thought I would share:

This appears to work OK for me on RedHat:

dir="/tmp/my_empty_dir"
[[ -d "${dir}" && -z "$(find "${dir}" -not -path "${dir}" -print -quit)" ]] && echo "${dir} is empty"

In this example:

First ensure dir exists -d "$dir", otherwise this will return empty (we will also see an error sent to stderr).

However it's likely you would need to test for this separately, as a "not empty" result is likely to mean "contains files" (which is not correct)

AND

Find: (find $dir -not -path $dir -print -quit):

  • Find everything in $dir
  • Exclude the directory $dir from the resulting output
  • Print the first result (something else within $dir)
  • Quit immediately (only return the first result).

BEWARE the -path parameter takes a "pattern", so if you are expecting special characters (eg: *, [, ]) these would need to be escaped Eg:

dir='/tmp/test[dir]'
dirpath='/tmp/test\[dir\]'
find "${dir}" -not -path "${dirpath}" -print -quit

During my test, this also successfully found hidden files. ($dir/.hidden)

Find returns 0 regardless of whether anything is found, and I don't currently see a simpler way to test this, so:

As per other examples I also wrapped this in:

Empty: [[ -z "$result" ]] to test if the result is blank.

NOT Empty: [[ ! -z "$result" ]] to test if the result is not blank.

Yes, the braces around ${dir} are not really required, but I thought it best to help handle this use case

dir="/tmp/"
[[ -d "${dir}subdir" ...
1

Without calling utils like ls, find, etc.:

Inside dir:

[ "$(echo *)x" != '*x' ] || [ "$(echo .[^.]*)x" != ".[^.]*x" ] || echo "empty dir"

The idea:

  • echo * lists non-dot files
  • echo .[^.]* lists dot files except of "." and ".."
  • if echo finds no matches, it returns the search expression, i.e. here * or .[^.]* - which both are no real strings and and have to be concatenated with e.g. a letter to coerce a string
  • || alternates the possibilities in a short circuit: there is at least one non-dot file or dir OR at least one dot file or dir OR the directory is empty - on execution level: "if first possibility fails, try next one, if this fails, try next one"; here technically Bash "tries to execute" echo "empty dir", put your action for empty dirs here (eg. exit).

Checked with symlinks, yet to check with more exotic possible file types.

  • this is a nice one, basically uses the globs suggested in the BashFAQ here -- perhaps wrap it in a function to make it more digestable? Also not sure whether the shoptions listed in the FAQ are needed – SpinUp __ A Davis Jun 14 '22 at 17:56
  • This test misses files named with 2 leading dots, for example ..a – db-inf Apr 15 '23 at 11:08
1

Another find solution, (which doesn't rely on other tools), and should be pretty fast:

if (( "$(find /directory/to/check/ -mindepth 1 -printf '1\n' -quit)" )); then
  echo The directory is not empty
else
  echo The directory is empty
fi
smac89
  • 474
0

For any directory other than the current one, you can check if it's empty by trying to rmdir it, because rmdir is guaranteed to fail for non-empty directories. If rmdir succeeds, and you actually wanted the empty directory to survive the test, just mkdir it again.

Don't use this hack if there are other processes that might become discombobulated by a directory they know about briefly ceasing to exist.

If rmdir won't work for you, and you might be testing directories that could potentially contain large numbers of files, any solution relying on shell globbing could get slow and/or run into command line length limits. Probably better to use find in that case. The best find solution I can think of goes like

is_empty() {
    test -z "$(find "$@" -maxdepth 0 -not -empty 2>&1)"
}

This works for the GNU and BSD versions of find but not for the Solaris one, which lacks both -maxdepth and -empty options (-empty does what it says; -maxdepth 0 forces find to examine only files and directories supplied explicitly as arguments rather than descending recursively into any directories so supplied).

The slightly convoluted combination of test -z with -not and error message redirection makes is_empty useful for testing multiple directories at once: it will return true only if find emits no output and no errors, which will happen only when all the files it's asked to process exist and are empty regular files or directories (given no arguments at all, it tests the current directory).

If you want a test that returns true only when all its arguments identify empty directories and false when any either don't exist or are non-directories, you could use

is_empty_dir() {
    test -z "$(find "$@" -maxdepth 0 -not \( -empty -type d \) 2>&1)"
}
  • Not a good idea. The OP simply wanted to test if the directory was empty or not. – Chris Davies May 02 '18 at 14:05
  • One of the best answers, both rmdir and is_empty() solutions are clean and efficient and support space-containing paths, unlike the majority of other answers. I also like globbing – Alek May 31 '24 at 17:37
0

This work for me, to check & process files in directory ../IN, considering script is in ../Script directory:

FileTotalCount=0

    for file in ../IN/*; do
    FileTotalCount=`expr $FileTotalCount + 1`
done

if test "$file" = "../IN/*"
then

    echo "EXITING: NO files available for processing in ../IN directory. "
    exit

else

  echo "Starting Process: Found ""$FileTotalCount"" files in ../IN directory for processing."

# Rest of the Code
kenorb
  • 26,983
Arijit
  • 1
0

I made this approach:

CHECKEMPTYFOLDER=$(test -z "$(ls -A /path/to/dir)"; echo $?)
if [ $CHECKEMPTYFOLDER -eq 0 ]
then
  echo "Empty"
elif [ $CHECKEMPTYFOLDER -eq 1 ]
then
  echo "Not Empty"
else
  echo "Error"
fi
Alex Sano
  • 9
  • 1
0

I might have missed an equivalent to this, which works on Unix

cd directory-concerned
ls * > /dev/null 2> /dev/null

return-code (test value of $?) will be 2 if nothing or 0 something found.

Note this ignores any '.' files and will probably return 2 if any of these exist without any other 'normal' filenames.

Anthony Kong
  • 5,366
Tim
  • 1
0

More solutions with find

# Tests that a directory is empty.
# Will print error message if not empty to stderr and set return
# val to non-zero (i.e. evaluates as false)
#
function is_empty() {
    find $1 -mindepth 1   -exec false {} + -fprintf /dev/stderr "%H is not empty\n" -quit
    # prints error when dir is not empty to stderr
    # -fprintf /dev/stderr "%H is not empty\n"
    #
    # -exec false {} +
    # sets the return value (i.e. $?) to indicate error
    #
    # --quit
    # terminate after the first match

}

examples

#!/bin/bash
set -eE # stop execution upon error

function is_empty() { find $1 -mindepth 1 -exec false {} + -fprintf /dev/stderr "%H is not empty\n" -quit }

trap 'echo FAILED' ERR #trap "echo DONE" EXIT

create a sandbox to play in

d=$(mktemp -d) f=$d/blah # this will be a potention file

set -v # turn on debugging

dir should be empty

is_empty $d

create a file in the dir

touch $f ! is_empty $d

this will cause the script to fail because the dir is not empty

is_empty $d

this line will not execute

echo "we should not get here"

output

[root@sysresccd ~/sandbox]# ./test

dir should be empty

is_empty $d

create a file in the dir

touch $f ! is_empty $d /tmp/tmp.aORTHb3Trv is not empty

this will cause the script to fail because the dir is not empty

is_empty $d /tmp/tmp.aORTHb3Trv is not empty echo FAILED FAILED

0

Accepted answer is good, although in bash I prefer shorter syntax:

if [[ `ls -A "$dir"` ]]; then
    echo Non empty
else
    echo Empty
fi

Also since I almost always set shopt -s nullglob in scripts, it seems more natural to me to use pure bash constructions, for example like this:

for nonEmpty in "$dir"/{,.}*; do break; done

if [[ $nonEmpty ]]; then echo Non empty else echo Empty fi

or this, if suitable:

for nonEmpty in "$dir"/{,.}*; do
    echo Non empty
    break
done

It's might be better since it does not use a subshell. Here "$dir"/{,.}* expands to "$dir"/* "$dir"/.* according to the Brace Expansion, see man bash

Alek
  • 125
0

This is all great stuff - just made it into a script so I can check for empty directories below the current one. The below should be put into a file called 'findempty', placed in the path somewhere so bash can find it and then chmod 755 to run. Can easily be amended to your specific needs I guess.

#!/bin/bash
if [ "$#" == "0" ]; then 
find . -maxdepth 1 -type d -exec findempty "{}"  \;
exit
fi

COUNT=`ls -1A "$*" | wc -l`
if [ "$COUNT" == "0" ]; then 
echo "$* : $COUNT"
fi
-1

EDIT: Disregard the below, I was indeed mistaken.

The -s check will report true if a directory exists and has a size greater than zero, which is true for any directory. It does not reliably indicate whether the dir is empty.

Thanks to the commenters for pointing this out. I've left the answer and the comments as it may be useful for others to see that -s doesn't work for this.


I could be mistaken, but I think this check should be sufficient:

[[ -s /path/to/dir ]] && echo "Dir not empty" || echo "Dir empty"
Lou
  • 660
  • 1
    -1: Bash 5.0 and 5.1.4 (Debian) return true for rm -r test 2>/dev/null; mkdir test; [[ -s test ]] && echo "true" – xebeche Jul 26 '22 at 12:49
  • 1
    bash manual says that the -s conditional is true if its argument exists and has a size greater than zero, which will be true for any directory in most filesystems; file size for a directory is not reliably correlated with entry count. – flabdablet Jun 07 '24 at 19:58
  • Thanks for the clarifications, that's helpful to know. Will leave this up with the listed caveats – Lou Jun 17 '24 at 08:02