41

I'm trying to create a Bash script that will delete everything in my .waste directory. I have a basic script I wrote but I want it to first check if the .waste directory has contents, and if so, to echo out a simple "Folder already empty!" message. I'm not too savvy about if and if else statements, and I don't know what the [ ] equation needs to check for presence.

Basic code:

#! /bin/bash
echo "The files have been deleted:"
cd /home/user/bin/.waste/
ls
rm -rf /home/user/bin/.waste/*

(P.S. not sure if the asterisk is correct at the end, I did try the script with it and I recall it deleted everything in the bin directory as well)

codeforester
  • 39,467
  • 16
  • 112
  • 140
Syler
  • 477
  • 1
  • 6
  • 13

3 Answers3

51

You can check if a directory is empty using find, and processing its output:

#!/bin/sh
target=$1
if find "$target" -mindepth 1 -print -quit 2>/dev/null | grep -q .; then
    echo "Not empty, do something"
else
    echo "Target '$target' is empty or not a directory"
fi

That is:

  • Use find to find the first filesystem entry under $target (-mindepth 1), print it (-print), and stop processing (-quit)
    • Redirect stderr to suppress any error messages (= noise)
  • Check if the output of the find command is empty using grep -q .
    • grep -q . will exit after processing at most one character. If it sees a character it exits with success, if it doesn't (its input is empty) then it exits with failure.
  • If the output of the find command is not empty, then the directory is not empty, and grep -q . exits with success.
  • If the output of the find command is empty, then $target is either an empty directory, or not a directory (does not exist), and grep -q . exits with failure.

The reason we have to rely on the stdout of find rather than its own exit code directly is that there's no way to make the find command use distinguishable exit codes in case files were found or not.

Instead of piping to grep -q, another alternative would be to capture the output of find and check if it's an empty string or not.

#!/bin/sh
target=$1
if [ "$(find "$target" -mindepth 1 -print -quit 2>/dev/null)" ]; then
    echo "Not empty, do something"
else
    echo "Target '$target' is empty or not a directory"
fi

Capturing command output like this uses a sub-shell. I think the solution using grep is probably faster, but I haven't tested it.

janos
  • 120,954
  • 29
  • 226
  • 236
  • @HermanTorjussen You probably didn't mean redirect the "output" but stderr. I put there two examples, one with the redirect and one without, on purpose. I'm hoping the OP can decide for himself if the error message is clutter or useful. – janos Dec 08 '13 at 18:19
  • @evilotto You're partly right. But `ls -U` is not portable: it does different things in GNU and BSD systems. See my updated answer with a much better solution that's portable. – janos Aug 20 '14 at 19:04
  • Alas, the `find -quit` option is not recognized on some Linux distros, such as Angstrom. I ended up having to write something like `if [ -d "$target" ] && [ -z "$(ls -A "$target")" ]; then ...` – Urhixidur Oct 02 '14 at 20:35
  • 1
    It is sometimes useful to recall that rmdir will do no harm to a non-empty directory. So if the goal happens to be to remove a directory only if it is empty, then `rmdir dir &>/dev/null` may fill the bill, the exit status informing you whether it was empty (and is now gone) or not (and is still there). – Ron Burk May 09 '16 at 17:59
  • You can also remove the `-print` option and `grep`, then test the result: `if find "$target" -mindepth 1 -quit 2> /dev/null; then echo "not empty"; else echo "empty or non-existent"; fi;` – devstuff Jun 02 '18 at 03:10
  • @devstuff `find /some/existing/empty/dir -mindepth 1 -quit` exits with success, so it doesn't fit the bill. It's unfortunate that we cannot rely on the exit code of `find` here :( – janos Jun 02 '18 at 04:36
  • Oops, my bad. I meant to use `-empty` instead of `-quit` (like @kdubs), so it should be: `if find "$target" -maxdepth 0 -empty 2> /dev/null; then echo "empty or non-existent"; else echo "not empty"; fi;` (this works correctly with the find included with MacOS Sierra). – devstuff Jun 04 '18 at 16:05
  • @devstuff that still doesn't work at all. `find "$target" -maxdepth 0 -empty 2> /dev/null` exits with success for empty and non-empty target too, on both OSX and Linux. @kdubs does something different, he uses `find` to print the answer, which does work. It seems we cannot use `find` to exit with a different code for empty and non-empty directories, and consequently we cannot use `find` directly in conditionals. We have to either process its output, or make it produce output like @kdubs did. – janos Jun 04 '18 at 17:32
  • Yeah, you're right, not enough coffee today :-(. Banged away for a bit and tested that this works on MacOS and Ubuntu: `if [ -n "$(find "${HOME}/tmp/zz" -mindepth 1 -print -quit 2> /dev/null)" ]; then echo "Non-empty"; else echo "Empty or non-existent"; fi;` - this is what I *meant* to post originally, to avoid the piping to `grep`. Now I have to find and fix my script that was using this bad test. – devstuff Jun 04 '18 at 19:26
  • 2
    @devstuff so basically you trade a pipe to a sub-shell. I would expect the pipe to be faster. For what it's worth, I completely rewrote my answer to make it cleaner, thanks for the extra push. – janos Jun 04 '18 at 20:43
  • @LoMaPh I don't understand your comment. I re-read my answer but I don't see where is a the confusing sentence. Can you please quote it to clarify? – janos May 07 '19 at 07:23
17

GNU find will let you do this

find . -maxdepth 0 -empty -exec echo {} is empty. \;

pretty quick and no pipes

kdubs
  • 1,596
  • 1
  • 21
  • 36
  • My `find` on Solaris has no `maxdepth` option... how can I achieve the same? – dokaspar Nov 14 '14 at 12:46
  • FYI, does not work on Ubuntu 16.04 server if the directory does not exist (returns error to terminal or exits script with standard "No such file or directory" error. – MrPotatoHead Feb 04 '19 at 18:14
6

Sorry, I don't have enough rep to answer Dominik's comment with a comment, so this is the best I can do...

My find on Solaris has no maxdepth option... how can I achieve the same? – Dominik Nov 14 at 12:46

I can't say for sure on earlier versions, but on Solaris 10 or better:

find . ! -name . -prune 
Reese Murdock
  • 91
  • 1
  • 3
  • 2
    A simple bash snippet to do what you require: ``` #!/bin/bash if [ ! -d directory ] || [ $(ls -l directory | wc -l) -lt 2 ] then echo Folder was empty fi ``` – oyelaking Oct 09 '19 at 18:50
  • I would do: if [ ! -d directory ] || [ $(ls -A directory | wc -l) -eq 0 ] then echo Folder empty fi – Daenerys Oct 31 '19 at 17:55