152

rm -rf /some/path/* deletes all non-hidden files in that dir (and subdirs).

rm -rf /some/path/.* deletes all hidden files in that dir (but not subdirs) and also gives the following error/warning:

rm: cannot remove directory: `/some/dir/.'
rm: cannot remove directory: `/some/dir/..'

What is the proper way to remove all hidden and non-hidden files and folders recursively in a target directory without receiving the warning/error about . and ..?

TRiG
  • 331
  • 1
  • 3
  • 18
Jake Wilson
  • 1,623
  • 2
  • 11
  • 6

14 Answers14

171

* matches all non-dot-files, .[!.]* matches all dot files except . and files whose name begins with .., and ..?* matches all dot-dot files except ... Together they match all files other than . and ... If any of these three patterns matches nothing, it expands to itself; rm -f doesn't care about non-existent arguments, so this doesn't matter.

rm -rf -- ..?* .[!.]* *

You can also use find. This is more complex but has the advantage of working even if there are so many files that the wildcards above would expand beyond your system's command line length limit.

find . -name . -o -prune -exec rm -rf -- {} +

You may find it clearer to remove and recreate the directory. This has the advantage (or downside, as the case may be) of resulting in an empty directory even if another program is concurrently creating files in the original directory.

Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
  • 14
    This should be the accepted answer, as it prevents parent traversal and possible deletion. – rbellamy Jun 12 '15 at 17:06
  • The `find` alternative returns "success" even if some file is not successfully deleted; not good for script. – Franklin Yu Jul 29 '16 at 07:22
  • With regards to your second `find` command, the manpage for find states "Because -delete implies -depth, you cannot usefully use -prune and -delete together." -- yet you use `-prune -delete`? – Doktor J Aug 18 '16 at 21:29
  • @DoktorJ Indeed, `-prune` doesn't do anything here. And on reading back I see that I didn't answer the question correctly: I took care not to recurse, but the question explicitly asks for recursive deletion. I've corrected my answer. – Gilles 'SO- stop being evil' Aug 18 '16 at 21:53
  • I'd find `find . ! -name . -prune -exec rm -rf {} +` less confusing (even if strictly equivalent) – Stéphane Chazelas Jun 13 '17 at 12:12
  • Very nice, but your globbing is bash-specific, though (which the question isn't). – haylem Aug 01 '17 at 14:05
  • @haylem Nothing in this answer is specific to bash. – Gilles 'SO- stop being evil' Aug 01 '17 at 14:08
  • @Gilles: Maybe, my bad. I should have said that zsh refuses this syntax. – haylem Aug 01 '17 at 14:12
  • 3
    @haylem In zsh, you need to write `.[^.]*` instead of `.[!.]*` when history substitution is enabled (which by default is the case interactively but not in scripts), because zsh parses `!` as a history reference. But in zsh you wouldn't need that in the first place, you can just use `*(D)` to include dot files (without `.` or `..`) in the wildcard match. – Gilles 'SO- stop being evil' Aug 01 '17 at 14:29
  • I've tried the `find` approach: `find . -name . -o -prune -exec rm -rf -- {} +`. Is it trying to delete everything in my current directory?!! `rm: cannot remove './hobby/outdoors/RACCC WW Beginners Handbook 2016 Final.docx': Permission denied` – sakovias Dec 28 '17 at 15:02
  • @sakovias Yes, it's trying to delete everything in your current directory, including subdirectories. That was the whole point. Of course, it's limited by permissions. If some files can't be deleted, it'll only delete the ones that can be., – Gilles 'SO- stop being evil' Dec 28 '17 at 16:18
  • 2
    `rm -rf /path/to/subdirectory/{..?*,.[!.]*,*} 2>/dev/null` – bjd2385 Jun 06 '18 at 07:00
  • I am using zsh: I tried `rm -rf /home/user/somedir/{,.[^.],..?}*` and i got `zsh: no matches found: /home/user/somedir/..?*` . Then @Gillies i tried `rm -rf /home/user/somedir/*(D)` and it worked `*(D) to include dot files (without . or ..) in the wildcard match` – Santhosh Sep 07 '19 at 17:34
  • This perhaps simpler: `find /repo -mindepth 1 -maxdepth 1 -exec rm -r -- {} +`. `-mindepth` to prevent deleting the given directory, `-maxdepth` to avoid stuff that's already going to be deleted by `rm -r` – mpen Feb 07 '21 at 02:09
  • Is there an actual risk that `rm -rf .*` does parent traversal? In any flavour of UNIX? – Torsten Bronger Aug 31 '21 at 08:58
  • @TorstenBronger Not in any modern flavor, I think. They refuse to recurse over `..` so they'll only delete children of the current directory. But I wouldn't put it past some older implementations of `rm` to omit this check and therefore delete the current directory and its siblings before they try and fail to `rmdir("..")`. – Gilles 'SO- stop being evil' Sep 01 '21 at 06:41
  • 1
    Also worth noting that using `./*` or `-- *` would be safer in case a filename starts with a dash (`-`) so that it doesn't get interpreted as an option. – Paul P Mar 30 '23 at 09:42
59

You could always send error messages to /dev/null

rm -rf /some/path/.* 2> /dev/null

You could also just

rm -rf /some/path/
mkdir /some/path/

...then you won't have to bother with hidden files in the first place.

evilsoup
  • 6,727
  • 3
  • 33
  • 40
  • 4
    But what if I only want to delete the hidden files? – CMCDragonkai Jun 01 '14 at 13:52
  • 1
    @CMCDragonkai that should be a different question, but you can find the solution in Gilles' answer (`rm ..?* .[!.]*` should do it). – evilsoup Jun 01 '14 at 18:14
  • 40
    Doesn't deleting and recreating the directory pose the risk that the file permissions are not right afterwards (especially important in server environments). Who could one create the folder with the same permissions as before automatically? – Yo Ludke Sep 23 '15 at 08:46
  • 4
    @YoLudke You are absolutely correct, in many situations it doesn't matter but deleting the folder and recreating is not semantically equivalent to emptying that folder; so be careful doing that! – Thomas Dec 29 '15 at 05:43
  • 6
    Deleting the directory is not always possible. In my case the directory is a docker volume, and while i can do whatever I want inside the volume, i cant change the volume itself from within the container. – dovidweisz Jun 03 '19 at 15:15
35

Just realised this is the most convenient way in most Linux distros:

ls -A1 | xargs rm -rf

where

-A = list everything except . and ..

-1 = put every item in one line

godzillante
  • 466
  • 5
  • 6
26

Either change the dotglob option of your shell and use *, or use something like find.

find somedir -mindepth 1 -delete
Ignacio Vazquez-Abrams
  • 44,857
  • 7
  • 93
  • 100
  • 4
    Or you could simply `rm -rf /some/dir` and then create a new empty directory in its place. – tripleee May 26 '13 at 12:11
  • @tripleee, in such case permissions might differ in the result. – Artfaith Dec 18 '21 at 21:16
  • Sure, there are scenarios where you can't recreate it exactly if it didn't belong to you in the first place even though you had write permissions. But in most real-life scenarios all you need is check the permissions so you can set them back to what they were. – tripleee Dec 19 '21 at 12:04
  • Any downsides to this? Curious as to why this is not the top answer... – Xen Aug 26 '22 at 15:53
10

This should work just like @Gilles answer but more compact:

rm -rf {,.[!.],..?}*

or

rm -rf dir/to/files/{,.[!.],..?}*

should also add an if for usage in scripts just to be safe:

if [ -d "$DIR" ]; then
    rm -rf ${DIR}/{,.[!.],..?}*
fi
Artfaith
  • 442
  • 1
  • 7
  • 16
Paweł Prażak
  • 205
  • 2
  • 6
  • 1
    Oddly enough, as a bash alias on Ubuntu 16.04 LTS, the previous answer wasn't working. However, `alias cleandir='rm -rf {,.[!.],..?}*'` does. – Steven Ventimiglia Apr 14 '18 at 18:44
  • @StevenVentimiglia Interactions with `!` and history? What shell? – user2864740 Mar 08 '20 at 19:52
  • Flippin heck - NO. Do not ever write rm -rf $x/..... It $x evaluates to empty you will delete a whole lot more than you want to. And believe me, and some point, now or later, it WILL be empty. just find another way - do a cd to $DIR like: `if cd $DIR # NO TRAILING SLASH then if [ "/" != "$(pwd)" ] rm -rf ./ – david collier Jan 10 '22 at 18:11
6

I suggest you experiment with

Turn-ON dots (hidden files)

  • set dotglob

    shopt -s dotglob

Turn-OFF dots

  • unset dotglob

    shopt -u dotglob

This method worked exactly as I wished for a copy command that was missing the hidden directories.

    shopt -s    dotglob
    cp    -rvn  ./$from/*  ./$too/
    shopt -u    dotglob

So I did a remove (delete), and oops ...

    shopt -s    dotglob
    rm -fr ../message_splitter--044a/*
    shopt -u    dotglob

... that works too!

It occurs to me that you dear reader can't see the message_splitter directory. Any way it has a .svn folder that needs to be removed, and copied Into.

From man page ...

dotglob If set, bash includes filenames beginning with a `.' in the results of pathname expansion.

references:

will
  • 480
  • 1
  • 6
  • 12
  • Behaviour of `shopt` [is unspecified](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_01_01). This answer may be useful for a subset of shells, but you didn't specify which ones. – Toby Speight Apr 07 '23 at 12:07
5

What is the proper way to remove all hidden and non-hidden files and folders recursively in a target directory without receiving the warning/error about . and ..?

Assuming the directory in question is ./dir, then

rm -rf ./dir

would remove all files in ./dir, including hidden files and directories, recursively, and including the ./dir directory itself.

If you do not want to delete the directory itself, then you may just recreate it afterwards, or use

find ./dir -mindepth 1 -delete

or if you find does not support -delete,

find ./dir -mindepth 1 -depth -exec rm -rf {} ';'

Using -mindepth 1 allows you to keep the top-level directory ./dir.

Kusalananda
  • 320,670
  • 36
  • 633
  • 936
  • Note, if the directory itself is the mount point for a filesystem, eg `/mnt/usbmemorystick` (if your goal is to delete all the files on a filesystem), then you can't just delete that directory itself. – Craig McQueen Nov 02 '22 at 01:55
  • @CraigMcQueen In that case the speediest alternative may be to unmount the file system, recreate it, and mount it again. – Kusalananda Nov 02 '22 at 04:22
  • Possibly, although that comes with its own set of challenges, such as making sure to recreate the filesystem with the same parameters. Some filesystems have some subtle parameters. Eg for ext4, the inode size, with consequences for Y2038 compatibility. – Craig McQueen Nov 03 '22 at 03:11
4

Find is your friend.

find ! -name '.' ! -name '..' -delete

% find ! -name '.' ! -name '..'
./test
./test4
./test4/.test6
./test3
./.test5
./test2
% find ! -name '.' ! -name '..' -delete    
% find ! -name '.' ! -name '..'     
%             

If you wish to use recursively search something other your current directory ($PWD), then add a path right after the find command; e.g., find /path ! -name '.' ! -name '..' -delete. If you only want to descend n number of directories, then use the -maxdepth n option right after the /path parameter.

The above command was tested on an Ubuntu 13.04 system. Will likely work on other, modern linux systems.

laebshade
  • 2,136
  • 13
  • 17
  • to delete all directories in the current directory you could do ```find . ! -name '.' ! -name '..' -type d -delete``` – Andy Jul 03 '16 at 04:01
2

Why nobody mentions:

rm -rf * .*
Qian Chen
  • 779
  • 4
  • 7
  • 18
0

What about using find with both -maxdepth and -mindepth? This can be also run outside the directory you want to clear.

find target/ -maxdepth 1 -mindepth 1 -exec rm -fr -- {} +;

Here's a complete example:

mkdir -p target/{plain{0..3},.hidden{0..3}}; 
touch target/{plain{0..3},.hidden{0..3}} target/{plain{0..3},.hidden{0..3}}/{plain{0..3},.hidden{0..3}};
tree -a;
find target/ -maxdepth 1 -mindepth 1 -exec rm -fr -- {} +;
tree -a;
0

Shorter version

rm -r * .*

rm: remove

-r: recursively

*: all directories

.*: all hidden directories ( ex: .git, .gitignore )

use an optional force flag ( --force or -f ). Check the usage here.

rm -rf * .*

Note: ( for zsh users )
The above command doesn't work partially i.e If it can't find files/ directories matching any of * or .* the other one won't execute.

You can fix this using the command

setopt no_nomatch

zsh commands are scoped to a session(/tab). ( if you open a new session you will still face the issue ).

add this line to .zshrc file to apply for all sessions.

ps: restart iterm after making changes to .zshrc

  • You forgot to explain how this avoids the warnings for `.` and `..`. – Toby Speight Apr 07 '23 at 12:03
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Apr 07 '23 at 17:16
  • I used the command recently, it didn't give any warning that was mentioned in the question. Should I add this? – Anjan Talatam Apr 08 '23 at 06:15
-1

If you don't mind an error

# rm -rf will force remove a file $(ls -la) lists all files and allows rm to iterate over the results.
rm -rf $(ls -la)

or, 2> /dev/null suppresses the error thrown about deleting . ..

rm -rf $(ls -la) 2> /dev/null
wattry
  • 99
  • 3
  • Thanks for contributing an answer to this question. You can improve the helpfulness of this and any other answers you provide in the future by including what each peice of the commands you have used do. – user1794469 Jan 03 '20 at 18:54
  • You will be aware that `ls -la` outputs quite a lot of things that are not filenames. Trying to delete `drwxr-xr-x` will likely fail, and a string like `-rw-r--r--` would be taken as a set of options by `rm`. Additionally, this would fail to delete anything that has spaces, tabs or newlines in their names. – Kusalananda Jan 03 '20 at 21:42
-1

Here is an alternative :

rm -rf folder/** 
rm -rf folder/.** 
Jack'
  • 99
  • 2
-2

Try this code:

Dirlist=``find /some/path/ -type d -name '.[^.]*'`
for HiddenDir in $Dirlist
do
    rm -rf $HiddenDir
done
Kevdog777
  • 3,194
  • 18
  • 43
  • 64
siz
  • 1