This question is kind of a phase II to the first question I posted at here
I have a directory that contains a bunch of sub-directories, .zip files, and other random files not contained within a sub-directory.
I'd like a command line script to remove all sub-directories from within the parent directory, but keep all zip files and loose files that don't belong to any sub-directories. All of the sub-directories have content, so I believe I'd need to force their deletion with the -f command.
So basically, a command that looks inside the parent directory (or the current directory), deletes all folders from within it, but keeps all other content and files that are not a folder or contained within a folder.
I understand that deleting items from the command line requires special care, but I have already taken all necessary precautions to back up remotely.
6 Answers 6
In BASH you can use the trailing slash (I think it should work in any POSIX shell):
rm -R -- */
Note the --
which separates options from arguments and allows one to remove entries starting with a hyphen - otherwise after expansion by the shell the entry name would be interpreted as an option by rm
(the same holds for many other command line utilities).
Add the -f
option if you don't want to be prompted for confirmation when deleting non-writeable files.
Note that by default, hidden directories (those whose name starts with .
) will be left alone.
An important caveat: the expansion of */
will also include symlinks that eventually resolve to files of type directory. And depending on the rm
implementation, rm -R -- thelink/
will either just delete the symlink, or (in most of them) delete the content of the linked directory recursively but not that directory itself nor the symlink.
If using zsh
, a better approach would be to use a glob qualifier to select files of type directory only:
rm -R -- *(/) # or *(D/) to include hidden ones
or:
rm -R -- *(-/)
to include symlinks to directories (but because, this time, the expansion doesn't have trailing /
s, it's the symlink only that is removed with all rm
implementations).
With bash
, AT&T ksh
, yash
or zsh
you can do:
set -- */
rm -R -- "${@%/}"
to strip the trailing /
.
-
Beautiful! This works perfectly. I didn't realize it was so easy to do. Would you mind elaborating on the -r vs. -R and what those commands do? Many thanks.Evster– Evster2013年03月22日 21:28:31 +00:00Commented Mar 22, 2013 at 21:28
-
-r
,-R
,--recursive
are synonyms meaning "remove directories and their contents recursively".peterph– peterph2013年03月22日 21:39:00 +00:00Commented Mar 22, 2013 at 21:39 -
can you explain what -- and */ do?amphibient– amphibient2013年03月22日 21:42:08 +00:00Commented Mar 22, 2013 at 21:42
-
7You can also say
rm -R ./*/
- It also avoids problems with hyphen names.l0b0– l0b02013年03月23日 09:08:09 +00:00Commented Mar 23, 2013 at 9:08 -
2Just be careful not to change the order of the * and the /. rm -R -- /* does something totally different that you may not want to happen :|ChrisOdney– ChrisOdney2017年07月07日 13:18:37 +00:00Commented Jul 7, 2017 at 13:18
In addition to the wildcard way, you can also use find
(at least GNU find) to do this:
find -mindepth 1 -maxdepth 1 -type d -print0 | xargs -r0 rm -R
As with other find
lines, you can run the first part (find -mindepth 1 -maxdepth 1 -type d
) to see a list of directories that will be removed.
Portably (POSIXly), you'd use the -exec cmd {} +
syntax instead of -print0 | xargs -r0
, and -prune
do limit the depth:
find . ! -name . -prune -type d -exec echo rm -R {} +
(and remove the echo
to actually do it).
A safer option (here also assuming GNU mv
) is to do something like this:
find -mindepth 1 -maxdepth 1 -type d -print0 | xargs -r0 mv -i -t ../to-rm
# or (but beware of symlinks!)
mv -i -t ../to-rm -- */
# or
mv -i -- */ ../to-rm
any of which will move all the stuff into ../to-rm
instead of deleting it. You can verify it did what you wanted, them rm -Rf
that directory at your leisure.
-
+1 for the "CLI-Trash bin"
to-rm
andfind
which nicely handles directories beginning with a dot (in addition to any other cumbersome escaping). :)peterph– peterph2013年03月22日 22:24:49 +00:00Commented Mar 22, 2013 at 22:24 -
Thanks for the tip. I like the idea of moving to a separate directory before deleting. I'm working via Terminal on Mac OS X, and I believe I'd need to install some kind of GNU package to use the -mindepth and -maxdepth commands, correct?Evster– Evster2013年03月22日 22:25:14 +00:00Commented Mar 22, 2013 at 22:25
-
@Evster not sure if Mac OS X find supports those. I'd guess fink (is that still around?) could install a find that does. Or you could omit them, and then filter the produced file list using grep. Definitely use the mv option on that, istead of rm.derobert– derobert2013年03月22日 22:52:02 +00:00Commented Mar 22, 2013 at 22:52
You might want to create a script for some of these suggestions, especially rm -R -- */
, and keep it in your /usr/local/bin folder; or create an alias
in your ~/.bashrc file. Since it's so easy to mistype a command and break your system -- even a single letter and/or the order of letters can result in catastrophic consequences -- this would serve as a somewhat more robust solution than having to type the different options and arguments each time you want to accomplish this task.
Also, you may want to include the -i or --interactive=once
or -I or --interactive=always
option to your script/command which will serve as another tool to avoid the unintended deletions.
Furthermore, something like derobert suggested would be best; just copy/paste the script into a file/terminal-editor and adjust it to your specific needs, and the files/directories will be moved into a single directory (the contents of which you can check/verify) that you can simply remove by issuing the rm -rf
command.
Another option is to use a GUI-application, such as your file manager, and simply select all applicable files/folders you want to delete. Check your distribution's manual-pages if you don't have permissions.
Finally, if the folders are empty -- essentially simple filenames -- you can use the rmdir
command to delete them. It doesn't work for everything you may want to delete, but it will come in handy at times when you want to do some "house-cleaning". **You may want try the -p --ignore-fail-on-non-empty
option, which will allow you to delete certain sub-directories as well as their empty "parents"--the directories in which they reside.
-
That's a really good idea on creating a separate script I can call for this command. Can you explain what exactly the -i and -I options do? Thanks!Evster– Evster2013年03月23日 00:33:02 +00:00Commented Mar 23, 2013 at 0:33
-
-I option will prompt you once after you hit "Enter" to confirm that you want to remove this(when there are more than 3 files/folders "selected"); whereas the -i option will issue a prompt for each file, upon which you can simply confirm by tapping "y" and "Enter"--anything besides an affirmative response will result in that specific file being skipped during the removal process.ILMostro_7– ILMostro_72013年03月23日 00:57:28 +00:00Commented Mar 23, 2013 at 0:57
First you need to list the directories and then remove them, without hampering the normal files, ls -d */, only lists the sub-directories, and piping it with rm removes the directories and contents, keeping the loose files intact.
This command should do the job,
ls -d */ | xargs rm -rf
-
1What would be the benefit over
rm -rf -- */
?printf '%s0円' */ | xargs -r0 rm -rf --
would help (in shells whereprintf
is builtin and notls
) to work around arg list too large errors, butls -d */ | xargs rm -rf
doesn't add anything AFAICT except additional problems.Stéphane Chazelas– Stéphane Chazelas2017年09月18日 08:49:44 +00:00Commented Sep 18, 2017 at 8:49 -
1@AnthonyGeoghegan, yes for instance if there was a directory called
" .."
(space..
), it would remove the content of the parent directory with somels
implementations. And it's not only spaces, it's also other blank characters, newline, single quote, double quote, backslash and file names starting with-
.Stéphane Chazelas– Stéphane Chazelas2017年09月18日 09:20:55 +00:00Commented Sep 18, 2017 at 9:20 -
2@Ankit, you could start with mywiki.wooledge.org/ParsingLsStéphane Chazelas– Stéphane Chazelas2017年09月18日 09:22:54 +00:00Commented Sep 18, 2017 at 9:22
-
1When you fully understand @StéphaneChazelas' suggestion of using
printf
to generate a list where each name is terminated by the null character so it can be processed byxargs
with the-0
option, I’d advise that you edit your answer to update it with his variation. I’d be fairly confident that he’d be happy for you to use his code snippet if you understand it and have learned how it guards against undesirable behaviour caused by certain file names that could potentially exist.Anthony Geoghegan– Anthony Geoghegan2017年09月18日 15:28:16 +00:00Commented Sep 18, 2017 at 15:28 -
1Using
xargs
does have one advantage over the accepted answer (it works when there's a very large number of files/directories in the current directory) so there’s scope for editing this into a useful answer. Personally, I’ve found answering Stack Exchange questions to be a good learning experience – particularly when I get useful feed-back from other users.Anthony Geoghegan– Anthony Geoghegan2017年09月18日 15:29:34 +00:00Commented Sep 18, 2017 at 15:29
If you want to remove the main directory as well. Try below command:
rm -rf <directory name>
If you want to keep the parent directory intact but remove all its contents:
enter code here
-
OP want to keep top level directory.Archemar– Archemar2020年07月03日 07:29:41 +00:00Commented Jul 3, 2020 at 7:29
rm -rf <directory name>
. It works. Just typing rm <directory name>
just leads to number of sub questions in which each of the sub directory must be removed manually. But, rm -rf
helps in removing the whole directory along with its sub directories at once.
-
7The OP asked to keep the contents of the current directory.Stephen Rauch– Stephen Rauch2017年03月07日 18:39:29 +00:00Commented Mar 7, 2017 at 18:39
You must log in to answer this question.
Explore related questions
See similar questions with these tags.