Scan ~/bin and turn the scripts into Emacs commands
| emacsI 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 (-map (lambda (filename) (let ((function-name (intern (concat "my/shell/" (file-name-nondirectory filename))))) `(defun ,function-name (&rest args) (interactive) (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!
7 comments
Phil
2015-12-15T05:41:20ZFrom the title I assumed this was going to be about automatically using `
shell-command-on-region
' with the REPLACE argument (alaC-u M-|
), in order to seamlessly use your external programs as if they were elisp commands.Tamara Temple
2015-12-15T23:58:52ZPhil, you could do something like that as well, and it wouldn't be too different. Many of my scripts turn out to be filters I use in just that way.
Meddix Boh
2015-12-15T14:26:25ZThank you very much for your insights, as usual.
Just a minor point: -not is part of dash-functional
sachac
2015-12-16T02:35:13ZYes, I've found that I had to add that as a require to my config. =) Thanks!
Tamara Temple
2015-12-15T23:57:51ZRather a neat idea. Thanks!
native-human
2015-12-17T21:29:22ZI love this idea! Boosting the habit of doing all stuff from within emacs and not having to switch to other tools. I would love to see evolving that idea, e.g., also using this kind of shell to emacs lisp conversions for ~/bin/filters with shell-command-on-region. Thanks a lot for sharing it!
sachac
2015-12-21T20:23:15ZSlowly tweaking my/convert-shell-scripts-to-interactive-commands in http://sachachua.com/dotemacs to do stuff like that as I feel the need for them. =) I added regions because of a script I wrote to add stuff to my grocery list ( https://github.com/sachac/s... ). One of these days, I might even figure out a nicer way to pass arguments interactively!