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?

1 Answers1

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.

erikstokes
  • 12,686
  • 2
  • 34
  • 56