MH & nmh: Email for Users & Programmers

May, 2006

Removing and Recovering Messages

The rmm command removes one or more messages. Like most MH commands, if you don't give a message number, rmm acts on the current message.

How rmm Removes Messages

As explained in the Chapter Key Parts of the UNIX Filesystem, MH messages are stored as files in UNIX directories -- one message per file. By default, rmm adds a comma (,) or a hash mark (#) to the start of a message filename. The extra character hides the message from other MH commands and flags the message for a system program to remove later. (You or your system administrator have to set up an automatic procedure for actually removing these "removed" messages. It will probably use either of the UNIX commands at(1) or cron(8) with find(1)-- most likely, just a one-line command.) This method of removing messages can give you a grace period to recover messages you've deleted accidentally.

Recovering a Removed Message

Recovering an accidentally removed message takes a little work. This section explains how to recover a message that you've just deleted. At the end, there's an easier method.

Let's start this example by removing message 10:

    % rmm 10 +somefolder
    % scan 10
    scan: message 10 doesn't exist
    
Use these steps to get the message back:
  1. Change your current directory into the mail folder where your message was. The `mhpath +somefolder` gets the pathname of the folder. Use backquotes (`), not single quotes ('):
          % cd `mhpath +somefolder`
          
  2. If you know the old message number, use ls to look for a filename that's the same number with a comma or hash mark before it. For example, the removed message 10 would be named ,10 or #10 -- so you'd type ls ,10 or ls \#10. (The backslash (\) tells the shell not to treat the hash mark (#) as a comment character. This isn't needed on all shells.)

    If you don't know the message number, search through the deleted messages with grep for lines that start with the word Subject:. (You can also search for messages by From:, To:, and so on.) Use a wildcard file-matching pattern: \#* if your deleted messages have a hash mark before them, or ,* if they have commas. Also, be sure to use single quotes ('), not backquotes (`):

          % grep '^Subject:' ,*
              ...
          ,13:Subject: How to attain nirvana
          ,15:Subject: How to get to Newark
              ...
          
  3. When you find the file, you can restore the message by taking away the extra character at the start of the name -- use the mv command to rename the file. Let's say that the message you want to restore is ,13. First, use ls to be sure that there's no other file (message) named 13.
  4. Whew. You're done. Now, think about using an rmmproc like rmmer!

When mhn and mhbuild remove a draft message during message composition, they add a comma or hash mark before the name and .orig after the name. You can recover that "removed" draft with steps like the ones above -- and there's a shortcut script, too. See the section Recovering MIME Drafts.

Changing Your rmmproc

You can change the way that rmm works by specifying a message removing program in your MH profile. For instance, you can tell rmm to use the standard system file removing command, rm. That will actually remove your message permanently, right away, with no grace period. If that's what you want, put the following entry in your MH profile:

    rmmproc: /bin/rm
    

In nmh-1.0 and above, there's an easier way to tell rmm to actually remove the source message: use the -unlink switch on the rmm command line, or add it to the rmm: entry in your profile. So, for example, rmm -unlink 23 will completely remove message 23: no wasted disk space and no "grace period" to get it back.

WARNING: The nmh-1.0 version of rmm will effectively ignore the -unlink switch if you have an rmmproc defined in your MH profile. If you have an rmmproc, you'll have to edit your MH profile and either delete that entry or else use the rmmproc: /bin/rm explained above.

You can also set rmm to "remove" messages by moving them to a subfolder named DELETE, where a program can find them and remove them later. This is more convenient for most people because you can scan your removed messages and refile them back into the parent folder (where they came from). To find out about that, read the Section Improve rmm: use rmmer and put this entry in your MH profile:

    rmmproc: rmmer
    

Improve rmm: use rmmer

The default rmm command "removes" a mail message by renaming it with a leading comma (,), hash mark (#), or other character. This hides the message from MH commands, but it doesn't really remove the message file. A system program comes along later to actually remove the "removed" message. (The Section Periodic Cleanup, Checking, etc. with cron and at introduces those system programs.)

That scheme lets you recover a deleted message, but it's not very easy. Also, if you delete a message and then later delete another message with the same number, the first "deleted" message will be overwritten by the second one. The rmmer shell script changes that. For instance, when you type, say:

    % rmm
    
rmmer moves the deleted message to a subfolder named DELETE. For example, let's say that you deleted a message by accident. To get it back, you look in the DELETE subfolder. Your message will be the last one in the subfolder because you just removed it. You show the deleted message. To recover the deleted message, move it back to the parent folder (where it was before) with refile and a relative folder name. After you recover it, it'll be the last message in the folder. Here's an example:
    % rmm
    % show last @DELETE
    (Message inbox/DELETE:25)
        ...A message appears -- this is the one you "deleted"...
    % refile @..
    % show last @..
    (Message inbox:54)
        ...The same message appears -- now it's back in the parent folder...
    
Of course, you can scan the deleted messages, use pick on them, and so forth. You run a periodic job to clean out the DELETE subfolders -- look here for the rm_msgs scripts.

If you don't want rmmer to use a subfolder and instead you want it to put all the messages in a central +DELETE folder, you can call the program with the name rmmer_1, and that'll do it. (The script checks its name and uses that to decide where to move the messages. You use a UNIX link to give the same script more than one name.) If you use nmh, using a central +DELETE folder has another advantage: it can make recursive searches (with folder -recurse and flist -recurse) much faster. That's because nmh (version 0.17, at least) optimizes folder searches to skip folders that don't have subfolders.

Like the UNIX rm -i command, rmmer can prompt to be sure you want to remove messages. rmmer will only ask in folders where you've created a file named .rmmer.ask. The best way to make this file is with the touch command. It creates a zero-length file which doesn't take any disk space to store. For example, to make rmmer prompt in your archive folder:

    % touch `mhpath +archive`/.rmmer.ask
    
When you remove messages in that folder, you can type n (or just RETURN) to stop the removal:
    % rmm last:3
    rmmer: remove 91 94 95 in +archive? [ny](n) y
    
(Something interesting happens when you use refile to move a message out of a folder and then answer n when rmmer asks you. The refile command will already have linked the message into the destination folder by the time it runs the rmmproc. You'll end up with a link between the two folders, as if you'd used refile -link.)

Sometimes, rmmer shouldn't prompt, even with a .rmmer.ask file. For example, if a job is running without a terminal on its standard input, like cron, then rmmer will never prompt. If you never want rmmer to prompt (if you use xmh, for example), use one of the versions with .noask on the end of its name, rmmer.noask or rmmer_1.noask.

You don't actually type a name like rmmer or rmmer_1 to run this script. Instead, you put one of its names in your MH profile, for example:

    rmmproc: rmmer.noask
    
The rmm and refile commands will then run rmmer.noask to "delete" messages.

Because the rmmproc: might be called from a cron job where your shell's search path is short, it's a good idea to use the full pathname in your MH profile:

    rmmproc: /xxx/yyy/bin/rmmer
    
This program is explained in the Section Explanation of rmmer. You'll also need to set up a program to clean out your DELETE folders periodically; see the Section Periodic Cleanup, Checking, etc. with cron and at for tips.

Delayed Removal: drmm

I get plenty of mail. A lot of it is stuff I need to keep for a few days, "just in case." Some of it needs to stay in my folders until a particular time, like the end of the year. After that, I need to remember to clean it out. Worse, to figure out which mail should be removed and which I need to keep, I usually need to re-read a lot of the messages. On my schedule, this never happens.

It might be possible to make a system of folders where all the mail in a folder should be removed at a certain time (like the end of a project). But I've never found a good way to do that. What I've done, instead, is to make a shell script named drmm ("delayed rmm"). When I run the script, it prompts me for the date that the message(s) should be removed. That date is written into an X-Remove-After: header field. Once a week, a simple cron job named drmmer searches all my folders for messages to remove -- messages which have an X-Remove-After: header field with a date of that day or before.

I also have a scan Format File named scan.drmm. It displays both the date that a message was sent (as scan usually does) and also the date a message will be removable. For example:

    % scan -form scan.drmm
       1        09/09 To:joan@retcom.co  How to make a frozen gel pack<<First,
       2        01/06 Bill J Pitre       Removing duplicate messages (Bourne)
       ....
      80        01/17 Operator           Output from "cron" command<<Your "cro
      81 R02/06 01/20 To:Xavier Viaud    Read my email next week?<<Xavier, I'll
      82        01/24 To:webmaster@amer  Broken link to PIN update page<<Hello -- I
       ...
    
You can see that message 81 is marked for removal. (I could have scanned only the messages marked for removal by using pick --x-remove-after . instead.)

On the drmm command line, give the messages you want to mark for removal. The default is the current message. drmm prompts like this:

    % drmm
    drmm: type date to remove message (RETURN gives 25 Sep 1997):
    > 
    
At the angle bracket (>), you have four choices: If you need a reminder, use drmm -help. Normally, drmm tells you the date it's removing the messages; its -noverbose switch stops that.

This program, and the cron job named drmmer, are explained in the Section Explanation of drmm.