Is there any difference between rm -r and rm -R? - linux

Title is Description.
I just wonder there is any difference between rm -r and rm -R.
Linux man description is seemed like they are totally same(and one more, --recursive), but many people use -r and -R like they aren't same.
So I want to know about not only its functional difference but its practical(or conventional) difference.
Could anyone let me know about this?

According to the OpenGroup / POSIX specification for rm, the -r and -R options are equivalent.
Source: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/rm.html
This probably applies to all modern UNIX / Linux variants.
But it wasn't always that way:
The -R option did not exist in v5 UNIX; see here, page 97.
Or in v7 UNIX; see https://www.unix.com/man-page/v7/1/rm/.
It apparently was introduced in 4.2bsd; see https://linuxgazette.net/issue49/fischer.html ... as meaning the same as -r.
The reason for adding -R was for compatibility with other commands that use -R to mean recursive. (For example chmod ... where -r means "remove read permission".)

Its the same.
# rm --help | grep directories.
-r, -R, --recursive remove directories and their contents recursively
By default, rm does not remove directories. Use the --recursive (-r or -R)

The man-page mentions:
-r, -R, --recursive
remove directories and their contents recursively
So, at least on my machine, it's the same.

The two options are equivalent, by default, rm does not remove directories. And by using the --recursive (-r or -R) option to remove each listed directory, too, along with all of its contents.

Related

ZSH+GREP+REGEX. Why this snippet act as rm -r /

This is a little anecdote from earlier on why not running root is vital.
I was sorting my home directory and deleted a few compressed files I had, I wrote
ls . | grep -P 'zip|tar|7z' | xargs rm and thought, hey I could also write this as rm -r $(ls . | grep -P '...') I suppose.
The second part I didn't mean to use it since there was nothing to delete, it was morelike a mental exercise, I wrote it next to the last command with a 'divider' to visually compare them.
ls . | grep -P 'zip|tar|7z' | xargs rm **//** rm -r $(ls . | grep -P '...')
Being **//** the "divider" and ... the mental "substitute" for 'zip|tar..'
I thought this wouldn't run but to my surprise, it acted as rm -r /and tried to delete everything, luckily permissions saved me and nothing was deleted.
But I'm curious why it'd work that way,
my guess is that rm **//** somehow translated to rm / but I'm not sure.
In the zsh shell, **//** would expand to all names under / as well to all names below the current directory (recursively).
From an empty directory on my system:
$ echo **//**
/altroot /bin /boot /bsd /bsd.booted /bsd.rd /bsd.sp /dev /etc /extra /home /mnt /root /sbin /sys /tmp /tmp_mnt /usr /var /vol
Why? Well, **/ matches all directories recursively under the current directory. More importantly, it matches the current directory, but since the current directory's name is not available inside the current directory, there's no entry returned for that.
However, when you add a / to that to create **//, then you get a lone / back for the current directory. Again in an empty directory:
$ echo **//
/
Then, if you add a further ** to make **//**, you pick up all names from the root directory, together with all names from the current directory and below (directory names from the current directory and below would occur twice in the list).
Your xargs is calling
rm **//** rm -r $(ls . | grep -P '...')
If you're using GNU rm, it will helpfully rearrange the command line so that it is interpreted the same as
rm -r **//** rm $(ls . | grep -P '...')
What this does should now be clear.
If you want to delete all regular files in the current directory that have filename suffixes .zip, .tar or .7z, use
rm ./*.(zip|tar|7z)(.)
in the zsh shell. If want to do that recursively down into subdirectories, use
rm ./**/*.(zip|tar|7z)(.)
The glob qualifier (.) makes the globbing pattern only match regular files. You could even restrict it to files above a certain size, say 10MB, with ./**/*.(zip|tar|7z)(.Lm+10).
One difference is that the ls ... | xargs .... solution also works if there are really a lot of files involved, while your rm $( .... ) might produce a argument list too long error. But if this is not an issue in your case, an even simpler attempt would be (assuming here Zsh; I don't understand why you tagged this bash, since you explicitly refer to Zsh only in your question)
rm *(zip|tar|7z)*(N)
which would express your original statement; I believe however that you really meant
rm -- *.(zip|tar|7z)(N)
because the solution you posted would also remove a file tarpit.txt, for instance. The (N) flag is a frail attempt to treat the case, that you don't have any file matching the pattern. Without the (N), you would get an error message from Zsh, and rm would receive the unexpanded file pattern, and, since it is unlikely that a file of this name exists, would output a second error message. By using (N), Zsh would simply pass nothing in this case (without complaining), and in fact rm would be invoked without arguments. Of course you would then get a rm: missing operand on stderr, and if you don't like this, you can filter this message.
UPDATES:
As Kusalananda has pointed out in his/her comment, omitting the (N) would, by default, make zsh only print an error message, if no files match the pattern, but not cause rm to be invoked.
Also added the -- flag to rm to allow removal of, i.e., a file called -rf.tar.

How to remove a file with special characterictics

Hi I just created a file by mistake, doing a tar actually, anyway the problem I have is that I can't remove that file. It is called --exclude-tag-under=hey.txt
I am trying to use rm -rf command but it doesn't do the trick. this is the output
[root]# rm -rf '--exclude-tag-under\=hey.txt'
rm: unrecognized option '--exclude-tag-under\=hey.txt'
Try 'rm --help' for more information.
the problem here is that the command rm is recognizing the file as a flag and thats a problem, I've tried also
rm -rf *hey.txt
but it doesnt work neither
I've also tried to change the name of the file but its the same problem
Prepend ./ like this: rm ./--exclude-tag-under\=hey.txt
When in doubt, check the man pages.
Running man rm will give you the rm man page, which, on Linux and OpenBSD (the ones I have tested) at least, will have a section saying:
To remove a file whose name starts with a '-', for example '-foo', use
one of these commands:
rm -- -foo
rm ./-foo
Use rm -- --exclude-tag-under=hey.txt
$ ls
--exclude-tag-under=hey.txt
test
$ rm -- --exclude-tag-under=hey.txt
$ ls
test

Unix command deleted every directory even though not specified

I am very new to the unix. I ran the following command.
ls -l | xargs rm -rf bark.*
and above command removed every directory in the folder.
Can any one explained me why ?
The -r argument means "delete recursively" (ie descend into subdirectories). The -f command means "force" (in other words, don't ask for confirmation). -rf means "descend recursively into subdirectories without asking for confirmation"
ls -l lists all files in the directory. xargs takes the input from ls -l and appends it to the command you pass to xargs
The final command that got executed looked like this:
rm -rf bark.* <output of ls -l>
This essentially removed bark.* and all files in the current directory. Moral of the story: be very careful with rm -rf. (You can use rm -ri to ask before deleting files instead)
rm(1) deleted every file and directory in the current working directory because you asked it to.
To see roughly what happened, run this:
cd /etc ; ls -l | xargs echo
Pay careful attention to the output.
I strongly recommend using echo in place of rm -rf when constructing command lines. Only if the output looks fine should you then re-run the command with rm -rf. When in doubt, maybe just use rm -r so that you do not accidentally blow away too much. rm -ir if you are very skeptical of your command line. (I have been using Linux since 1994 and I still use this echo trick when constructing slightly complicated command lines to selectively delete a pile of files.)
Incidentally, I would avoid parsing ls(1) output in any fashion -- filenames can contain any character except ASCII NUL and / chars -- including newlines, tabs, and output that looks like ls -l output. Trying to parse this with tools such as xargs(1) can be dangerous.
Instead, use find(1) for these sorts of things. To delete all files in all directories named bark.*, I'd run a command like this:
find . -type d -name 'bark.*' -print0 | xargs -0 rm -r
Again, I'd use echo in place of rm -r for the first execution -- and if it looked fine, then I'd re-run with rm -r.
The ls -l command gave a list of all the subdirectories in your current present-working-directory (PWD).
The rm command can delete multiple files/directories if you pass them to it as a list.
eg: rm test1.txt test2.txt myApp will delete all three of the files with names:
test1.txt
test2.txt
myApp
Also, the flags for the rm command you used are common in many a folly.
rm -f - Force deletion of files without asking or confirming
rm -r - Recurse into all subdirectories and delete all their contents and subdirectories
So, let's say you are in /home/user, and the directory structure looks like so:
/home/user
|->dir1
|->dir2
`->file1.txt
the ls -l command will provide the list containing "dir1 dir2 file1.txt", and the result of the command ls -l | xargs rm -rf will look like this:
rm -rf dir1 dir2 file1.txt
If we expand your original question with the example above, the final command that gets passed to the system becomes:
rm -rf di1 dir2 file1.txt bark.*
So, everything in the current directory gets wiped out, so the bark.* is redundant (you effectively told the machine to destroy everything in the current directory anyway).
I think what you meant to do was delete all files in the current directory and all subdirectories (recurse) that start with bark. To do that, you just have to do:
find -iname bark.* | xargs rm
The command above means "find all files in this directory and subdirectories, ignoring UPPERCASE/lowercase/mIxEdCaSe, that start with the characters "bark.", and delete them". This could still be a bad command if you have a typo, so to be sure, you should always test before you do a batch-deletion like this.
In the future, first do the following to get a list of all the files you will be deleting first to confirm they are the ones you want deleted.
find -iname bark.* | xargs echo
Then if you are sure, delete them via
find -iname bark.* | xargs rm
Hope this helps.
As a humorous note, one of the most famous instances of "rm -rf" can be found here:
https://github.com/MrMEEE/bumblebee-Old-and-abbandoned/commit/a047be85247755cdbe0acce6f1dafc8beb84f2ac
An automated script runs something like rm -rf /usr/local/........., but due to accidentally inserting a space, the command became rm -rf /usr /local/......, so this effectively means "delete all root folders that start with usr or local", effectively destroying the system of anyone who uses it. I feel bad for that developer.
You can avoid these kinds of bugs by quoting your strings, ie:
rm -rf "/usr/ local/...." would have provided an error message and avoided this bug, because the quotes mean that everything between them is the full path, NOT a list of separate paths/files (ie: you are telling rm that the file/folder has a SPACE character in its name).

Everywhere that GHC/Haskell Platform installs

Assume I want to completely reinstall GHC/HP. I want to (as much for superstition as anything) delete anything and everything from previous installs. What do I actually need to delete (and where)?
Edit: I'm on OSX, but I'm more curious if this information is available in general, for all systems.
Edit2: So far we have:
OSX:
/Library/Frameworks/GHC.framework/
~/.cabal/
/usr/bin/ -- symlinks
I'll add to that (based on "prefix" defined here: http://www.vex.net/~trebla/haskell/sicp.xhtml#storage):
prefix/lib/
prefix/share/
prefix/bin/
prefix/share/doc/
/usr (/local) /lib/[ghc-version]
/usr (/local) /share/doc/ghc/html/libraries/ -- documentation
/usr (/local) /share/doc/ghc/
/usr (/local) /bin
/var/lib/[ghc-version]
/etc/[ghc-version]
~/.ghc/
Edit 3:
OS X:
~/Library/Haskell
Linux:
??
Windows:
??
Had to remove Haskell Platform on OS X recently. Most are cleaned up via Uninstaller:
sudo /Library/Frameworks/GHC.framework/Versions/Current/Tools/Uninstaller
These have to be cleaned up manually:
rm -r ~/.cabal
rm -r ~/.ghc
rm -r ~/Library/Haskell
Alternatively, as documented in
/Library/Haskell/doc/start.html
there is now a custom uninstall command with options,
/Library/Haskell/bin/uninstall-hs
In general, one can document the files created by any activity (installer, ...) by bracketing the activity in a work directory with
echo >timestamp
[activity]
sudo find -x / -newer timestamp -print >snapshot.txt
If you've installed a Haskell Platform since about 2012 on OS X, just run
uninstall-hs
and carefully read what it outputs. You'll need to run it again with the options it offers you. Run
uninstall-hs --help
for more options.
Below is my original answer, which will still work, but doesn't offer as many options and is a bit "ham fisted":
Warning: This script is extreme. It will remove even your custom config files for GHC and Cabal, and executables you've built that are still in ~/Library/Haskell or ~/.cabal. Use caution; review what it is about to do; have backups; caveat scriptor!
#!/bin/bash
set -x
sudo rm -rf /Library/Frameworks/GHC.framework
sudo rm -rf /Library/Frameworks/HaskellPlatform.framework
sudo rm -rf /Library/Haskell
rm -rf ~/.cabal
rm -rf ~/.ghc
rm -rf ~/Library/Haskell
find /usr/bin /usr/local/bin -type l | \
xargs -If sh -c '/bin/echo -n f /; readlink f' | \
egrep '//Library/(Haskell|Frameworks/(GHC|HaskellPlatform).framework)' | \
cut -f 1 -d ' ' > /tmp/hs-bin-links
sudo rm -f `cat /tmp/hs-bin-links`
You may want to add lines to save off and restore your config files like:
mv ~/.cabal/config /tmp/cabal-config 2>/dev/null || true
mv ~/.ghc/gchi.conf /tmp/ghci-config 2>/dev/null || true
and
mkdir ~/.cabal
mkdir ~/.ghc
cp /tmp/cabal-config ~/.cabal/config 2>/dev/null || true
cp /tmp/ghci-config ~/.ghc/gchi.conf 2>/dev/null || true
Bracket the rm lines with these. Though you may or may not want your old ~/.cabal/config if you are upgrading to newer stuff.
Note that this only deals with the current user's home directory. If you have multiple user accounts that all use Haskell, then the other accounts will need cleaning as well. (Repeat the lines involving ~.)
I am on OSX (Lion atm). I've got GHC in /Library/Frameworks/GHC.framework/ (current and previous versions). There are also some symlinks in /usr/bin, but these will be replaced by a new install.
If you have used cabal to (locally) install packages, you also may want to clean out ~/.cabal. If you have a recent cabal, you can easily reinstall all packages for the 'new' GHC version by using cabal install world and then look for directories matching previous version of GHC you had like so:
for package in `ls ~/.cabal/lib/`; do
if [ ! -d ~/.cabal/lib/${package}/ghc-7.0.3 ]; then
echo $package;
else
echo "OK for $package";
fi;
done
These should be safe to delete.
Hope this helps you somewhat.
uninstall-hs does some of the work for you; I'm not sure how much.

What is the safest way to empty a directory in *nix?

I'm scared that one day, I'm going to put a space or miss out something in the command I currently use:
rm -rf ./*
Is there a safer way of emptying the current directory's contents?
The safest way is to sit on your hands before pressing Enter.
That aside, you could create an alias like this one (for Bash)
alias rm="pwd;read;rm"
That will show you your directory, wait for an enter press and then remove what you specified with the proper flags. You can cancel by pressing ^C instead of Enter.
Here is a safer way: use ls first to list the files that will be affected, then use command-line history or history substitution to change the ls to rm and execute the command again after you are convinced the correct files will be operated on.
If you want to be really safe, you could create a simple alias or shell script like:
mv $1 ~/.recycle/
This would just move your stuff to a .recycle folder (hello, Windows!).
Then set up a cron job to do rm -rf on stuff in that folder that is older than a week.
I think this is a reasonable way:
find . -maxdepth 1 \! -name . -print0 | xargs -0 rm -rf
and it will also take care of hidden files and directories. The slash isn't required after the dot and this then will also eliminate the possible accident of typing . /.
Now if you are worried what it will delete, just change it into
find . -maxdepth 1 \! -name . -print | less
And look at the list. Now you can put it into a function:
function enum_files { find . -maxdepth 1 \! -name . "$#"; }
And now your remove is safe:
enum_files | less # view the files
enum_files -print0 | xargs -0 rm -rf # remove the files
If you are not in the habit of having embedded new-lines in filenames, you can omit the -print0 and -0 parameters. But i would use them, just in case :)
Go one level up and type in the directory name
rm -rf <dir>/*
I use one of:
rm -fr .
cd ..; rm -fr name-of-subdirectory
I'm seldom sufficiently attached to a directory that I want to get rid of the contents but must keep the directory itself.
When using rm -rf I almost always use the fully qualified path.
Use the trash command. In Debian/Ubuntu/etc., it can be installed from the package trash-cli. It works on both files and directories (since it's really moving the file, rather than immediately deleting it).
trash implements the freedesktop.org trash specification, compatible with the GNOME and KDE trash.
Files can be undeleted using restore-trash from the same package, or through the usual GUI.
You could always turn on -i which would prompt you on every file, but that would be really time consuming for large directories.
I always do a pwd first.
I'll even go as far as to create an alias so that it forces the prompt for my users. Red Hat does that by default, I think.
You could drop the `f' switch and it should prompt you for each file to make sure you really want to remove it.
If what you want to do is to blow away an entire directory there is always some level of danger associated with that operation. If you really want to be sure that you are doing the right thing you could always do a move operation to some place like /tmp, wait for some amount of time to make sure that everything is okay with the "deletion" in place. Then go into the /tmp directory and ONLY use relative paths for a forced and recursive remove operation. Additional, in the move do a rename to "delete-directoryname" to make it easier not to make a mistake.
For example I want to delete /opt/folder so I do:
mv /opt/folder /tmp/delete-folder
.... wait to be sure everything is okay - maybe a minute, maybe a week ....
cd /tmp
pwd
rm -rf delete-folder/
The most important tip for doing an rm -rf is to always use relative paths. This keeps you from ever having typed a / before having completed your typing.
There's a reason I have [tcsh]:
alias clean '\rm -i -- "#"* *~'
alias rmo 'rm -- *.o'
They were created the first time I accidentally put a space between the * and the .o. Suffice to say, what happened wasn't what I expected to happen...
But things could have been worse. Back in the early '90s, a friend of mine had a ~/etc directory. He wanted to delete it. Unfortunately he typed rm -rf /etc. Unfortunately, he was logged in as root. He had a bad day!
To be evil: touch -- '-rf *'
To be safe, use '--' and -i. Or get it right once and create an alias!
Here are the alias I am using on macOS. It would ask for every rm command before executing.
# ~/.profile
function saferm() {
echo rm "$#"
echo ""
read -p "* execute rm (y/n)? : " yesorno
if [ $yesorno == "y" ]; then
/bin/rm "$#"
fi
}
alias srm=saferm
alias rm=srm

Resources