Scan ~/bin and turn the scripts into Emacs commands

I want to automate little things on my computer so that I don’t have to look up command lines or stitch together different applications. Many of these things make sense to turn into shell scripts. That way, I can call them from other programs and assign keyboard shortcuts to them. Still, I spend most of my computer time in Emacs, and I don’t want to think about whether I’ve defined a command in Emacs Lisp or in a shell script. Besides, I like the way Helm lets me type parts of commands in order to select and call them.

Emacs Lisp allows you to define a macro that results in Emacs Lisp code. In this case, I want to define interactive functions so I can call them with M-x. In case I decide to call them from Emacs Lisp, such as (my/shell/rotate-screen "left"), I want to be able to pass arguments. I’m also using dash.el to provide functions like -filter and -not, although I could rewrite this to just use the standard Emacs Lisp functions.

Here’s the code that scans a given directory for executable files and creates interactive functions, and some code that calls it for my ~/bin directory.

(defmacro my/convert-shell-scripts-to-interactive-commands (directory)
  "Make the shell scripts in DIRECTORY available as interactive commands."
  (cons 'progn
           (lambda (filename)
             (let ((function-name (intern (concat "my/shell/" (file-name-nondirectory filename)))))
               `(defun ,function-name (&rest args)
                  (apply 'call-process ,filename nil nil nil args))))
           (-filter (-not #'file-directory-p)
                    (-filter #'file-executable-p (directory-files directory t))))))

(my/convert-shell-scripts-to-interactive-commands "~/bin")

Let’s see how that goes!