1

I use emacs as my editor with mutt. I would like to run a python code on the /tmp/mutt-* files upon exit (that is called email-process, and that is written and works). For instance, in the case of vim, it is possible to have the following set up in the .vimrc to do this.

augroup autocom
   autocmd!
"    " executes my command on quit
     autocmd VimLeave /tmp/mutt-* !/home/username/bin/email-process %
augroup END

Is it possible to do the same for emacs? If so, how do I set this up in my .emacs file?

The following has some similarity with my question:

Execute external script upon save when in a certain mode?

However, I am interested in executing the command when I exit and only for /tmp/.mutt* files for now. I looked up kill-emacs-hook and start-process and call-process suggested below but I could not tell how to use it. I am not very familiar with emacs configuration files.

From the example here,

Can I call a python script from emacs?

it seems to me that I should define my script as something like:

(defun email-process ()
  "Process cpmposed outgoing email"
  (when (equal file-name "/tmp/mutt-*")
    (shell-command "pathname")))

(add-hook 'kill-hook #'email-process)

However, when I do this, I am unable to get out of the compose window, and indeed I get:

Symbol's value as a variable is void: file-name

So, it appears to me that there is something wrong with how I am checking for the filename to activate the kill-hook on. Any suggestions?

Thanks for your help, and please feel free to let me know if my question needs more clarification.

Drew
  • 75,699
  • 9
  • 109
  • 225
user3236841
  • 113
  • 4
  • 2
    There is a `kill-emacs-hook` to which you could attach your own custom command, whether that be a shell script, a `start-process` function with custom command, a `call-process` function with a custom command, etc. .... – lawlist Oct 02 '21 at 05:40
  • There’s no variable called `file-name` (unless you happen to create one), but there is an accessor function named `buffer-file-name`. Called with no arguments, like `(buffer-file-name)`, it returns the name of the file visited by the current buffer, if the current buffer is visiting a file. I think that you will want your hook function to examine all buffers, so you will want to write code that looks like `(dolist (buffer (buffer-list)) (when (string-prefix-p "/tmp/mutt-" (buffer-file-name buffer)) …)`. Once you get this working, make sure you post it as an answer! – db48x Oct 03 '21 at 01:26
  • https://emacs.stackexchange.com/tags/elisp/info – Drew Oct 04 '21 at 21:25

1 Answers1

2

What @lawlist is describing in the comment can be implemented by writing a function to do whatever it it that you want to do and then by modifying the kill-emacs-hook to execute the function when the hook is run.

TL;DR

Add this to your emacs init file:

;; define a function that runs the given command line
(defun email-cleanup ()
   (call-process-shell-command "/home/username/bin/email-process /tmp/mutt-*"))

;; add the above function to the hook
(add-hook 'kill-emacs-hook #'email-cleanup) 

Although I copied the path of the executable from your question, you will probably have to modify it (at least the username part) to fit your setup.

I've tested with a dummy script and it works; but if you run into problems, let me know in a comment and I'll try to sort it.

A more detailed explanation

Hooks are variables whose value is a list of functions. Running the hook evaluates each function in the hook in sequence. Emacs defines many different hooks for different operations. You can get a list by saying C-h v -hook TAB (and maybe wait a few seconds: it's a long list). Each of those hooks is run under very specific circumstances. It is one of the most important methods of customizing emacs behavior, so it is worth familiarizing yourself with them.

In this particular case, you want to find a hook that is run when emacs exits. Look in the Exiting Emacs section of the Emacs manual: the best way to do that is by asking Emacs itself: C-h i g(emacs)Exiting - learning how to use the built-in documentation is probably the best thing you can do to make your life easier and more pleasant while using Emacs. See where it says

To further customize what happens when Emacs is exiting, see *note (elisp)Killing Emacs::.

That is a link so you can click on it. You'll see the description of kill-emacs-hook on that page. That's how you find (or at least that's one way to find) the hook you want.

Once you've found the hook, the next thing is to write a function to do what you want. In this case, you want to write a function that executes a shell command: that's why @lawlist in the comment mentions start-process and call-process: they are ways to execute an external program from inside emacs (similar to vi's ! mechanism). In this case however, you will want to run a shell in order to expand the glob in the argument to a full list of matching files. The best function to use then is call-process-shell-command (see its doc string with C-h f call-process-shell-command): the more basic ones (call-process and start-process exec the program directly, so there is no shell to do the expansion).

So the function email-cleanup defined above runs a shell and gives it the command line in the function. The shell expands the glob and runs the program with that expanded list of arguments.

Then all we have to do is add the function to the hook.

EDIT: It turns out that the OP's script only handles one file for each invocation, so it cannot be invoked from the shell as email-process /tmp/mutt-* and expect to handle more that one file. It has to be invoked as for f in /tmp/mutt-* ;do email-process $f ;done. It also overwrites its input file with its output (which is generally a bad idea IMO, but I'm not addressing that in this comment).

So try with this function:

(defun email-cleanup ()
   (call-process-shell-command "for f in /tmp/mutt-* ;do /home/username/bin/email-process $f ;done"))

Personally, I would change the script to loop over its arguments and go back to the first form of the function, but that's out of scope for this question.

If this still does not work, then please tell me the exact form of the command you execute in the shell so that it does what you want. All of this has nothing to do with emacs of course: it is how your script has to be invoked from the shell.

NickD
  • 27,023
  • 3
  • 23
  • 42
  • My thanks to you for your help, and my apologies. However, this does not appear to run e-mail process, or at least have any effect on the file. – user3236841 Oct 03 '21 at 01:41
  • Did you fix up the path so that it points to the right executtable? Edited the question to point that out (in the TL;DR section). – NickD Oct 03 '21 at 01:47
  • Yes, I did. I have verified that it is correct and the path leads to the executable which is as here: http://ianfinlayson.net/posts/10-mutt (and works with vim). – user3236841 Oct 03 '21 at 02:58
  • I have even gone out and applied the executable to the /tmp/mutt-* file and it does work when applied using the commandline so I do not understand why it does not work for me. – user3236841 Oct 03 '21 at 03:11
  • Btw, I exit emacs using 'C-x C-c'. Btw, thanks for the detailed explanation! – user3236841 Oct 03 '21 at 03:18
  • I edited the answer with some additional information. See if that helps. – NickD Oct 03 '21 at 10:27
  • Wait: is there a single file called `/tmp/mutt-*`? Does the asterisk not imply globbing?· – NickD Oct 03 '21 at 10:50
  • Thank you. This works, and I now understand what was going on previously. I am happy to change the script. I don't know why/when mutt keeps old /tmp/mutt-* files hanging around. – user3236841 Oct 03 '21 at 13:09