7

Say I have a shell command foo that I want to bind to a key. I could do for example:

(global-set-key (kbd "C-c f") (lambda () (interactive) (shell-command "foo")))

I would like to be able to pass an argument to foo. An argument will be the name of a file in the current directory, so I would like to take an advantage of shell auto-complete. The preferable behavior would be for C-c f to open shell in the minibuffer with foo already entered so that I can just add the filename and hit RET. How can I achieve that?

2
  • 1
    As a hint — start with the manual for the (interactive) form
    – BRPocock
    Commented Oct 23, 2015 at 21:24
  • 1
    If this question is answered then please consider accepting the answer. This is still on the unanswered list. Thx.
    – Drew
    Commented Feb 11, 2019 at 18:04

1 Answer 1

7

As you seem to have already noticed, a function need the interactive form before it can be bound to a key. interactive doesn't just tell Emacs the function is a command, it is also tells Emacs where to get the functions arguments from. From C-h f interactive:

Specify a way of parsing arguments for interactive use of a function. For example, write (defun foo (arg buf) "Doc string" (interactive "P\nbbuffer: ") .... ) to make ARG be the raw prefix argument, and set BUF to an existing buffer, when ‘foo’ is called as a command.

A typical case is (interactive c) where c is a single-letter string describe the type of argument you expect. "f" means "existing file".

An example function would then be

(defun test-command (file)
  (interactive "f")
  (message file))

Or to run a shell command foo on a file:

(defun foo-file (file)
  (interactive "f")
  (shell-command (format "foo %s" (shell-quote-argument file))))

Notice that we wrap the file name with shell-quote-argument which will quote the file name so that it's safer to call shell commands on it in case your file names have spaces or special shell characters. Safety First. (thanks @YoungFrog for the suggestion)

Bind it to a key and call it. Emacs will prompt you for a file name in the usual way, using whatever kind of completion you have set up, and then pass the name (as a string) into the function as file.

9
  • You want to add a bit of quote-shell-argument in there, for the cases where the file name has quotes, spaces, newlines or other weird characters.
    – YoungFrog
    Commented Oct 23, 2015 at 21:48
  • Good idea. Safety first.
    – erikstokes
    Commented Oct 23, 2015 at 21:49
  • Unfortunately, this doesn't work. foo-file seems to add quotation marks around the file path and foo doesn't recognize it. Commented Oct 24, 2015 at 23:54
  • The quotes are added by shell-quote-argument. The shell should be eating them.
    – erikstokes
    Commented Oct 24, 2015 at 23:58
  • I see, for some reason my shell doesn't eat them. If I remove shell-quote-argument it does work... except when there are spaces in the path of the file. Commented Oct 25, 2015 at 0:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.