5

I'm cleaning an output directory before I put things in it. I want to track the output directory in git with a .gitkeep file, but this keeps on getting deleted with my rm -rf "$OUT_DIR" line in a bash script.

Is there a way to remove the contents of a directory but keep either hidden files or somehow just keep that .gitkeep file?

5

By default bash doesn't glob dot-files, so to remove everything but hidden files in bash, using rm:

rm *

Sample output:

~/tmp$ ls -la
total 8
drwxrwxr-x  2 user user 4096 giu 11 20:00 .
drwxr-xr-x 21 user user 4096 giu 11 08:26 ..
-rw-rw-r--  1 user user    0 giu 11 20:00 .1
-rw-rw-r--  1 user user    0 giu 11 20:00 2
-rw-rw-r--  1 user user    0 giu 11 20:00 3
-rw-rw-r--  1 user user    0 giu 11 20:00 4
-rw-rw-r--  1 user user    0 giu 11 20:00 5
~/test$ rm *
~/tmp$ ls -la
total 8
drwxrwxr-x  2 user user 4096 giu 11 20:00 .
drwxr-xr-x 21 user user 4096 giu 11 08:26 ..
-rw-rw-r--  1 user user    0 giu 11 20:00 .1

To remove everything but .gitkeep in bash, enabling globbing for dot-files and using rm:

shopt -s dotglob
rm !(.gitkeep)

Sample output:

~/tmp$ ls -la
total 8
drwxrwxr-x  2 user user 4096 giu 11 20:19 .
drwxr-xr-x 21 user user 4096 giu 11 08:26 ..
-rw-rw-r--  1 user user    0 giu 11 20:19 1
-rw-rw-r--  1 user user    0 giu 11 20:19 2
-rw-rw-r--  1 user user    0 giu 11 20:19 3
-rw-rw-r--  1 user user    0 giu 11 20:19 4
-rw-rw-r--  1 user user    0 giu 11 20:19 5
-rw-rw-r--  1 user user    0 giu 11 20:19 .gitkeep
-rw-rw-r--  1 user user    0 giu 11 20:19 .hidden
~/tmp$ shopt -s dotglob
~/tmp$ rm !(.gitkeep)
rm: cannot remove ‘.’: Is a directory
rm: cannot remove ‘..’: Is a directory
user@user-X550CL:~/tmp$ ls -la
total 8
drwxrwxr-x  2 user user 4096 giu 11 20:20 .
drwxr-xr-x 21 user user 4096 giu 11 08:26 ..
-rw-rw-r--  1 user user    0 giu 11 20:19 .gitkeep
4

Try this:

shopt -u dotglob        # disable globbing for dot files
ls *

If everything looks okay, replace ls by rm.

  • 3
    You probably want ls -d * to avoid listing contents of any matching directory. – roaima Jun 11 '15 at 19:32
0

The problem is that a recursive remove rm -r * behaves differently to an non-recursive rm * with respect to hidden files. The first removes them but the second does not (with default shell options). But, of course, recursive is needed in order to remove subdirectories.

My solution is to use move which does leave hidden files in place, instead of remove. It is a couple more lines but makes sense when you do it (no odd options to remember).

mkdir ../deleteme
mv * ../deleteme
rm -rf ../deleteme
0

rm * works fine if your directory doesn't contain subdirectories (see the answer of @kos). However, if you need a recursive solution that also removes all subdirectories, then (as @meesern has pointed out) rm -r * is not what you want, since it does remove hidden files and subdirectories.

Instead, you can use find to list and delete non-hidden files and subdirectories (adapted from this answer):

find YOUR_DIRECTORY -not -path '*/\.*' -delete

This will delete all non-hidden files in YOUR_DIRECTORY and all subdirectories that are empty after the non-hidden files have been removed.

It will not remove content inside a hidden directory (e.g. YOUR_DIRECTORY/.foo/bar/baz). As a result, you might get some warnings like

find: cannot delete 'some/directory/here': Directory not empty

But you can safely ignore them.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.