29

Intention:

rm -rf string*

Problem:

rm -rf string *

The first case is a legitimate and common use of rm, a small typo can cause a lot of problems in the second case. Is there a simple way to smartly protect against an accidental trailing or leading wildcard?

  • 8
    With a recent and well configured shell (zsh or bash) I would rather take the habit of pressing the tab key (to trigger auto-completion) before pressing the return key – Basile Starynkevitch Jan 15 '15 at 8:25
  • 2
    touch -- -i will create a file -i that will be passed to rm as the parameter -i when you include a wildcard in your arguments. – Martin Schröder Jan 16 '15 at 17:30
  • @MartinSchröder: If you happen to be in the directory where the file exists. – Dennis Williamson Jan 16 '15 at 19:28
  • @MartinSchröder That will only work if you type rm *. If you typ rm name *, it will expand to rm name -i other names. -i won't be processed as an option because it's not before the filenames. – Barmar Jan 16 '15 at 19:40
  • 1
    Typically, a user only does this once. – tedder42 Jan 20 '15 at 23:03

11 Answers 11

37

A DEBUG trap could be written to cancel commands that look suspicious. The following, or code similar to it, can be added to your ~/.bashrc:

shopt -s extdebug
checkcommand() {
  if [[ $BASH_COMMAND = 'rm -r'*' *' ]]; then
    echo "Suppressing rm -r command ending in a wildcard" >&2
    return 1
  fi
  # check for other commands here, if you like
  return 0
}
trap checkcommand DEBUG

Adjust the logic to taste.

(I don't actually expect this approach to be useful -- too many ways to mess up a command destructively to find them testing one-by-one -- but it provides a literal answer to the question).

47

There is no way to a totally bulletproof a system. And adding "Are you sure?" prompts to things is both counterproductive and leads to "Of course I'm sure." kneejerk reactions.

A trick I picked up from a book in years past is to first do ls -R blah* then, do rm -fr blah* if and only if the listing that ls turned up hit what I wanted hit.

It's easy enough to do the ls command first, then , delete the ls -R and replace with rm -fr.

The final question is, "If the information was valuable to you, where's your backup?"

  • 4
    Rather than up arrow you can use ^ls -R^rm -rf^ and maybe set as an alias – exussum Jan 14 '15 at 13:26
  • FWIW, this is the ls -R is what I basically do all the time, but it is so instinctual at this point it slipped my conscious mind when I formed by answer. So +1 for that. – JakeGould Jan 14 '15 at 19:10
  • This also has the advantage that it won't do harm if you bump the enter key, or bump your mouse and paste in something with newlines. I sometimes enter things in a less efficient order just for more safety, e.g. cat > .log, then go back in and paste or yank the filename before the .log. So at no point do I have > valuablefile on the cmdline. – Peter Cordes Jan 15 '15 at 8:34
  • I use rm aliased to rm -i, so I often just compose my rm command and then put a \ at the beginning of the line. (\rm avoids alias-expansion for rm, since part of the word was quoted). If I needed a -r or -f, yeah I sometimes start with ls instead of rm, or just leave off the -rf part. (edit, how do you get a backslash code-formatting? bquote bslash bslash bquote fails. – Peter Cordes Jan 15 '15 at 8:37
8

Can you train yourself to use, say, rmrf in place of rm -rf?

If so, this bash function will provide you a chance to see what would actually happen before confirming the command:

rmrf() { echo rm -rf "$@"; read -p "Proceed (y/N)? "; [ "${REPLY,,}" = y ] && rm -rf "$@"; }

To make this function permanent, add this line to the ~/.bashrc file on each computer you use.

Comparison with the common rm alias

It is common to define an alias:

alias rm='rm -i'

This has two limitations:

  1. If you come to depend on this alias, then you will in for a shock when you are on a machine or in an environment that does not have it. By contrast, trying to run rmrf on a machine without that function will return a harmless command not found.

  2. This alias is no help in your case because the -f option that you supply on the command line overrides the -i option in the alias.

  • 4
    Not a bad idea. But it depends on this function being installed and available on any systems this user is using. So let’s say they login into a remote server for some work and run the wrong rm -rf command, what happens? Creating pseudo functions like this ultimately just complicate a simple solution: Just be more careful and understand what permissions are. – JakeGould Jan 14 '15 at 5:13
  • 4
    @JakeGould And don't use rm -f unless you really need to. – a CVn Jan 14 '15 at 12:51
  • 1
    Why bother with rmrf, as opposed to just creating a shell function rm that checks for the arguments -r or -rf, and enacts special logic in that case, otherwise directly calling command rm "$@" to invoke the real rm command? – Charles Duffy Jan 14 '15 at 18:18
  • 3
    You use a different name so you don't shoot yourself in the foot when your override isn't there. And I agree with @MichaelKjörling, you don't need -f if you don't have -i. Leaving off -f gives you extra warning before removing a readonly file, for example. – Peter Cordes Jan 15 '15 at 8:26
6

If I am in a situation where deleting the wrong files is a really big deal, one of the things I've done is make a trashcan folder, like mkdir trashcan, and then I have a script rmTrashcan which has a rm -rf trashcan/* or rm -rf * or similar, written very carefully and checked several times.

That way, if I make a mistake, the mistake is on a mv command, not a rm command. Once I do a ls and am confident of exactly what I am deleting, a rmTrashcan actually does the dirty work safely.

It was also tremendously convenient for a situation where I had to delete backups. I could mv an entire month of backups into the trashcan, mv the few I wanted to keep (1st of month, 15th of month) back into the backups, then rmTranshcan the rest. Doing a similar command is hard to do with rm on its own, and doing it this way let me ls the backups to be confident in what files I am leaving behind (rather than just enumerating the files I intend to delete)

4

Instead of bothering with two commands (ls and rm), I think a simpler and easier way is to use find:

find . -maxdepth 1 -name "string*" -delete

If you accidentally type "string *" it will delete files named string chars and string letters (which is what you wanted anyway), but it won't grab every file like * in a shell will.

Also, you can leave off the -delete to see which files it's going to delete, then hit up arrow and type -delete easier than typing ^ls -R^rm -rf or other nonsense.

  • A find will do a nested search for files that match instead of the just the explicit parameters declared on the commandline. I think this misses the mark. I could be wrong though. – killermist Jan 14 '15 at 19:19
3

Is there a simple way to smartly protect against an accidental trailing or leading wildcard?

Not really. It’s proposed in another answer you could create a custom command to add a prompt before executing a task. But the problem with this custom command is it must be consciously installed on systems you are working on. And even if it is installed, the problem is your reflex to hit y to get the task done with could come into play.

Meaning this is all a user interface issue even if it is on a text/command line level. How many safety nets do you expect there to be to protect you from doing something you shouldn’t? It’s like a pair of scissors: If you are somehow negligent and slip and cut your hand while intending to cut cloth or paper, who is at fault? Or even traffic lights and stop signs: There’s really nothing stopping a driver from running a light or ignoring traffic signs other than an acute awareness of what might happen if they do engage in such risky behavior.

That said the best, realistic solution lies in system permissions for users as well as groups. That is the best/only real safety net to protect a user from themselves.

If you are working on a system where you are the only one accessing it, you might be tempted to just chmod 777 everything, but that is not the way a rational system is setup. Instead permissions should be something like 755 for all directories and 644 for all non-executable files. Executable files should be 755 at the least, but maybe even 744 if you only want others to read but not run the files.

2

Try composing your initial rm command without the -f flag, but with -i instead, such that rm will prompt you for each file it intends to delete. For small recursive deletions, you can hold down the y key, once you're sure the command has been typed correctly. For large deletions, you can abort the operation, and use commandline history to carefully change the -i to a -f.

  • You actually have to press y[RETURN] every file, there's nothing you can hold down with GNU rm. Editting out the -i is the way to go, once you have the command typed properly. Then you get a prompt only on a read-only file, and maybe other things. – Peter Cordes Jan 15 '15 at 8:28
  • 1
    Just use rm -I so that it only prompts once, and only for potentially dangerous deletions (i.e. of many files.) – Nick Matteo Jan 15 '15 at 17:57
  • 1
    In less constructive forums, my answer would have been something along the lines of "If your typing/proofreading is that bad, you've no business using 'rm -rf' with wildcards." I didn't know of the -I switch... must've been put in <20 years ago; 'bout the last time I did 'man rm'. :) – Nevin Williams Jan 15 '15 at 20:01
2

rm has -i and -I flags to confirm before every removal. In the past, some distributions have turned them on by default. This is a terrible idea. Give the user too many confirmation dialogs for normal operations and they'll begin to habitually confirm them. This just shifts the requirement to "be careful" (always a red flag) to a new and more annoying dialog. "Yes. Yes. Yes. Yes! YES! God damnit, stupid computer just delete the files YESYESYESYESYES--CRAP I MEANT NO! NOOOOOOO!" This is the "Yes, but I meant no" dialog problem. This answer provides a visual explanation of why confirmation dialogs come at the wrong time.

The sort of mistake you describe is a slip, "the performance of an action which was not what was intended". The user typically immediately recognizes the mistake and knows exactly how to fix it. Unfortunately, Unix does not give the user the opportunity, rm deletes the file immediately. Every other operating system solves this problem by allowing deletions to be undone, at least for a little while, by the use of the Trash.

There are various trash systems for Unix, and this answer is full of suggestions.

The question is to alias rm or not to alias rm. Pros to aliasing rm...

  1. You can't make the slip of forgetting to use the rm alternative.

Cons to aliasing rm...

  1. You might come to rely on it on systems which don't have it.
  2. Might cause problems when the disk is nearly full.
  3. Need infrastructure to periodically empty the trash.
  4. Have to be sure not to interfere with the expected behavior of rm in programs.
  5. Might not fully emulate rm.

If you follow the first argument too far, you wind up using vi (not vim, vi), csh (not tcsh, csh) and other antiquated utilities because they're universally available. Still, there is a danger of overcustomizing your environment. I prefer to take my utilities with me and to make that as easy as possible. YMMV.

Two and three are technical problems. They can be solved with a clever reaper job which checks the size of the trash and periodically cleans things up, similar to a tmpreaper. This can be a cron job, or a more clever version can make use of the various file system event infrastructures available on many desktop Linux distributions. This is not simple, and even harder to do efficiently. Its better to find an existing system than to try to make your own.

The fourth can be dealt with by making your new rm a shell alias, alias rm='trash', then it will not affect programs.

The fifth is a problem I leave for the reader to solve. rm doesn't have a lot of switches.

  • I have rm aliased to rm -i, but I often use \rm to get the unaliased behaviour. I type rm -i any time I'm PLANNING to say no to one of the args. I sometimes start the command with ls as the command, then only put in the \rm when I'm done. So there's no way I can accidentally hit return on rm -rf / instead of /.../file – Peter Cordes Jan 15 '15 at 8:22
  • rm -I or rm --interactive=once is far, far less annoying than rm -i, and still catches aything dangerous (it only prompts when there are more than 3 files, or if it's recursive, and just ONCE instead of FOR EACH FILE.) – Nick Matteo Jan 15 '15 at 17:55
  • @Kundor Yes, and it doesn't change the equation. It probably makes it more likely the user will ignore it. The user has a goal: delete stuff. The prompt says "are you sure you want to delete the stuff you just told me to delete?" The user is still fixated on the goal of deleting stuff, not verifying what is to be deleted, so they say "yes" without thinking. -i might give the user time to change goals. This answer on a related question lays it all out. – Schwern Jan 15 '15 at 20:40
  • @Schwern: but no one can possibly live with rm -i for more than 30 seconds without turning it off. Whereas rm -I only prompts when it's likely that you've screwed up; the prompt only comes up for big deletions. When you were trying to delete a couple of things and the prompt is given, you are taken aback and realize that you accidentally typed "foo *". – Nick Matteo Jan 15 '15 at 20:44
  • @Kundor Deleting more than three files and deleting recursively (the confirmation is disabled by the habitual -rf I just discovered) are poor proxies for detecting a likely screw up. Wanting to delete a sub-directory isn't a likely screw up. The message doesn't help because it just confirms what the user said to do without adding useful information at a point when the user is not looking to verify. "rm: remove 1 argument recursively?" "Yes, delete the directory, I just told you to do that! ... wait, which directory was that? CRAP!!!" – Schwern Jan 15 '15 at 20:53
2

I teach everyone about the !$ = last argument in the last command trick:

% ls job[XYZ].*
jobX.out1
jobX.out2
[rest of the matches]

% rm !$

This allows an inspection of the wildcard expansion, and then use of the exact same glob pattern without the possibility of inserting a space before the *.

Also I suggest people never cut and paste a globbing wildcard pattern on a potentially destructive command line. Because in my own hands that has been a problem :)

A tech in my lab just made this mistake last week (3 months of work) - and yes we had a backup (1-day behind).

0

Everyone seems to be saying, "Be more careful", "Don't make an alias/confirmation prompt because you'll become habituated to not paying proper attention to it".

Cool, and everything. I mean, I don't think you should make an alias for rm (nor rmrf, since you could easily screw that up and type the real command).

But why can't you make an alias/script and call it, say, remove and only feed it one argument (eg $1)? The wildcard should be $2, because of the inadvertent space (amiright?) and thus your script/wrapper/alias won't be fed the second one. Yes, you can only do one set of deletions (with a wildcard) at a time, but that's the price you'd pay.

If I were writing something nice, I might have it tell me the number of files and directories it's planning on deleting, and the total size of stuff I was deleting, and then ask for a confirm, but that might impede your flow. Maybe make that a flag option on remove? (-i). You might also wish to do a check $1 to see if it's just a single wildcard and ask for a confirmation, and list which directory you're actually in.


As an aside, there are a number of rm replacements out there. Many are trying to be compliant with the UI desktop trash. Some of those might be worth looking into.

  • 6
    Your "remove" alias will never remove more than one file at a time. You cannot use a wildcard, because the shell expands it before the command sees it. – hymie Jan 14 '15 at 16:11
  • So do an eval on it, and then pull that into the script? – user3082 Jan 15 '15 at 8:12
  • So you want to type remove \*? If your script lets the shell glob-expand its input, I don't see it helping much. – Peter Cordes Jan 15 '15 at 8:18
0

A very simple trick is to put -rf at the end: rm whatever* -rf
This reduce drasticaly the error rate, because you type more character after the *, so you have more time to see typos.
This does not solve everything. Just a simple everyday trick.

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.