dwim-shell-command
dwim-shell-command copied to clipboard
Emacs shell commands with DWIM behaviour
👉 [[https://github.com/sponsors/xenodium][Support this work via GitHub Sponsors]]
- Bring command-line utilities to your Emacs workflow
Use =dwim-shell-command-on-marked-files= to define new functions that apply command-line utilities to current buffer or [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Dired.html][dired]] files.
For example:
#+begin_src emacs-lisp :lexical no
(defun my/dwim-shell-command-convert-to-gif ()
"Convert all marked videos to optimized gif(s)."
(interactive)
(dwim-shell-command-on-marked-files
"Convert to gif"
"ffmpeg -loglevel quiet -stats -y -i '<
Can be applied as:
#+HTML:
This makes wrapping one-liners a breeze, so let's do some more...
** One-liners
#+begin_src emacs-lisp :lexical no
(defun my/dwim-shell-command-convert-image-to-jpg ()
"Convert all marked images to jpg(s)."
(interactive)
(dwim-shell-command-on-marked-files
"Convert to jpg"
"convert -verbose '<
(defun my/dwim-shell-command-convert-audio-to-mp3 ()
"Convert all marked audio to mp3(s)."
(interactive)
(dwim-shell-command-on-marked-files
"Convert to mp3"
"ffmpeg -stats -n -i '<
(defun dwim-shell-commands-http-serve-dir () "HTTP serve current directory." (interactive) (dwim-shell-command-on-marked-files "HTTP serve current dir" "python3 -m http.server" :utils "python3" :focus-now t :no-progress t)) #+end_src
** Multi-line scripts
#+begin_src emacs-lisp :lexical no
(defun dwim-shell-commands-image-view-location-in-openstreetmap ()
"Open image(s) location in map/browser."
(interactive)
(dwim-shell-command-on-marked-files
"Browse location"
"lat="$(exiftool -csv -n -gpslatitude -gpslongitude '<
** Pick your language
#+begin_src emacs-lisp :lexical no
(defun dwim-shell-command-csv-to-json-via-python ()
"Convert csv file to json (via Python)."
(interactive)
(dwim-shell-command-on-marked-files
"Convert csv file to json (via Python)."
"
import csv
import json
text = json.dumps({ "values": list(csv.reader(open('<
(defun dwim-shell-command-csv-to-json-via-swift ()
"Convert csv file to json (via Swift)."
(interactive)
(dwim-shell-command-on-marked-files
"Convert csv file to json (via Swift)."
"import Foundation
import TabularData
let filePath = "<
- Build a rich toolbox (or use mine)
While you may want to build your own command toolbox over time, I've also [[#my-toolbox][shared my toolbox]] (close to 100 commands).
If you create new command not found in my list, I'd love to hear about it. File an [[https://github.com/xenodium/dwim-shell-command/issues/new][issue]] or just ping me ([[https://indieweb.social/@xenodium][Mastodon]] / [[https://twitter.com/xenodium][Twitter]] / [[https://www.reddit.com/user/xenodium][Reddit]] / [[mailto:me__AT__xenodium.com][Email]]).
- A =shell-command=, =async-shell-command=, and =dired-do-shell-command= alternative
#+HTML:
** Run M-x =dwim-shell-command= to execute disposable [[https://en.wikipedia.org/wiki/DWIM][DWIM]] shell commands
- Asynchronously.
- Using noweb templates.
- Automatically injecting files (from [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Dired.html][dired]] or other buffers) or kill ring.
- Managing buffer focus with heuristics.
- Showing progress bar.
- Quick buffer exit.
- More reusable history.
- Which files
=dwim-shell-command= determines which file(s) you want the command to operate on.
If visiting a [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Dired.html][dired]] buffer, draw the marked file(s).
#+HTML:
If visiting a buffer with an associated file, use that.
#+HTML:
- noweb templates
Operate on drawn files using either the following:
- =<
>= (file path) - =<
>= (file path without extension) - =<
>= (extension) - =<
>= (generate a temporary directory) - =<<*>>= (all files joined)
- =<
>= (clipboard) For example:
With drawn files =path/to/image1.png= and =path/to/image2.png=
=convert <
> < >.jpg= expands to #+begin_src sh convert path/to/image1.png path/to/image1.jpg convert path/to/image2.png path/to/image2.jpg #+end_src
while =ls -lh <<*>>= expands to
#+begin_src sh ls -lh path/to/image1.png path/to/image2.png #+end_src
- Focus
=dwim-shell-command= creates a process buffer to capture command output, but neither displays nor focuses on it by default. Instead, it tries to guess what's more convenient to focus on.
While the process is busy, show a spinner in the minibuffer. No focus changes.
#+HTML:
After process is finished:
If there were any files created in the =default-directory=, jump to a [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Dired.html][dired]] buffer and move point to the new file (via [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Dired-Enter.html][dired-jump]]).
[[file:images/showme.png]]
If no new files were created, automatically switch focus to the process buffer and display its output.
#+HTML:
Note: You can prevent this automatic focus by prepending your command with whitespace.
" convert '<
>' '< >.jpg'" If the shell command caused any errors, offer to focus the process buffer and display its output.
#+HTML:
*** Easily create utilities
Command-line utilities like [[https://ffmpeg.org/][ffmpeg]] can be easily integrated into Emacs flows (without the need to remember any flags or parameters) by wrapping command invocations into functions and invoking via =M-x= (or your favorite binding). Same DWIM behavior from =dwim-shell-command= is inherited.
- Quick exit
Process buffers are read-only and can be quickly closed by pressing =q=.
- More reusable history Because of templates, command history becomes automatically reusable in other contexts.
#+HTML:
- Install
=dwim-shell-command= is available on [[https://melpa.org/#/dwim-shell-command][MELPA]].
[[https://melpa.org/#/dwim-shell-command][file:https://melpa.org/packages/dwim-shell-command.svg]]
- Install via M-x /package-install/.
- Require, set edit style, and add company backend:
#+begin_src emacs-lisp (require 'dwim-shell-command) #+end_src
Now you're ready to run
M-x =dwim-shell-command=
** use-package
Alternatively, can also install via [[https://github.com/jwiegley/use-package][use-package]], define your own commands and remap to =shell-command='s existing binding using something like:
#+begin_src emacs-lisp :lexical no (use-package dwim-shell-command :ensure t :bind (([remap shell-command] . dwim-shell-command) :map dired-mode-map ([remap dired-do-async-shell-command] . dwim-shell-command) ([remap dired-do-shell-command] . dwim-shell-command) ([remap dired-smart-shell-command] . dwim-shell-command)) :config (defun my/dwim-shell-command-convert-to-gif () "Convert all marked videos to optimized gif(s)." (interactive) (dwim-shell-command-on-marked-files "Convert to gif" "ffmpeg -loglevel quiet -stats -y -i '<
>' -pix_fmt rgb24 -r 15 '< >.gif'" :utils "ffmpeg"))) #+end_src - My toolbox
I'm including an optional package ([[https://github.com/xenodium/dwim-shell-command/blob/main/dwim-shell-commands.el][dwim-shell-commands.el]]), with all the command line utilities I've brought in over time. You can load this optional package via:
#+begin_src emacs-lisp :lexical no (require 'dwim-shell-commands) #+end_src
Note: =dwim-shell-command(s).el= gives you all commands, while =dwim-shell-command.el= provides only the building blocks.
Here are all the commands I've added so far...
#+BEGIN_SRC emacs-lisp :results table :colnames '("Command" "Description") :exports results (let ((rows)) (mapatoms (lambda (symbol) (when (and (string-match "^dwim-shell-commands" (symbol-name symbol)) (not (string-match "git-set-author-name-and-email-credentials" (symbol-name symbol))) (commandp symbol)) (push `(,(symbol-name symbol) ,(car (split-string (or (documentation symbol t) "") "\n"))) rows)))) (seq-sort (lambda (row1 row2) (string-greaterp (seq-elt row2 0) (seq-elt row1 0))) rows)) #+END_SRC
#+RESULTS: | Command | Description | |--------------------------------------------------------------+-----------------------------------------------------------| | dwim-shell-commands-audio-to-mp3 | Convert all marked audio to mp3(s). | | dwim-shell-commands-clip-round-rect-gif | Clip gif(s) with round rectangle. | | dwim-shell-commands-clipboard-to-qr | Generate a QR code from clipboard. | | dwim-shell-commands-copy-to-desktop | Copy file to ~/Desktop. | | dwim-shell-commands-copy-to-downloads | Copy file to ~/Downloads. | | dwim-shell-commands-docx-to-pdf | Convert docx(s) to pdf (via latex). | | dwim-shell-commands-download-clipboard-stream-url | Download clipboard URL. | | dwim-shell-commands-drop-video-audio | Drop audio from all marked videos. | | dwim-shell-commands-duplicate | Duplicate file. | | dwim-shell-commands-epub-to-org | Convert epub(s) to org. | | dwim-shell-commands-external-ip | Copy external IP to kill ring. | | dwim-shell-commands-files-combined-size | Get files combined file size. | | dwim-shell-commands-gif-to-video | Convert all marked gif(s) to video(s). | | dwim-shell-commands-git-clone-clipboard-url | Clone git URL in clipboard to
default-directory'. | | dwim-shell-commands-git-clone-clipboard-url-to-downloads | Clone git URL in clipboard to "~/Downloads/". | | dwim-shell-commands-git-delete-untracked-files | Delete untracked git files in
default-directory'. | | dwim-shell-commands-git-list-untracked-files | List untracked git files indefault-directory'. | | dwim-shell-commands-http-serve-dir | HTTP serve current directory. | | dwim-shell-commands-image-clear-exif-metadata | Clear EXIF metadata in image(s). | | dwim-shell-commands-image-exif-metadata | View EXIF metadata in image(s). | | dwim-shell-commands-image-horizontal-flip | Horizontally flip image(s). | | dwim-shell-commands-image-reverse-geocode-location | Reverse geocode image(s) location. | | dwim-shell-commands-image-scan-code | Scan any code (ie. qr, bar, etc) from image(s). | | dwim-shell-commands-image-to-grayscale | Convert all marked images to grayscale. | | dwim-shell-commands-image-to-icns | Convert png to icns icon. | | dwim-shell-commands-image-to-jpg | Convert all marked images to jpg(s). | | dwim-shell-commands-image-to-png | Convert all marked images to png(s). | | dwim-shell-commands-image-trim-borders | Trim image(s) border (useful for video screenshots). | | dwim-shell-commands-image-vertical-flip | Horizontally flip image(s). | | dwim-shell-commands-image-view-location-in-map | Open image(s) location in map/browser. | | dwim-shell-commands-image-view-location-in-openstreetmap | Open image(s) location in map/browser. | | dwim-shell-commands-join-as-pdf | Join all marked images as a single pdf. | | dwim-shell-commands-join-images-horizontally | Join all marked images horizontally as a single image. | | dwim-shell-commands-join-images-vertically | Join all marked images vertically as a single image. | | dwim-shell-commands-kill-gpg-agent | Kill (thus restart) gpg agent. | | dwim-shell-commands-kill-process | Select and kill process. | | dwim-shell-commands-macos-abort-recording-window | Stop recording a macOS window. | | dwim-shell-commands-macos-add-to-photos | Add to Photos.app. | | dwim-shell-commands-macos-bin-plist-to-xml | Convert binary plist to xml. | | dwim-shell-commands-macos-caffeinate | Invoke caffeinate to prevent mac from sleeping. | | dwim-shell-commands-macos-convert-to-mp4 | Convert to mov to mp4 | | dwim-shell-commands-macos-end-recording-window | Stop recording a macOS window. | | dwim-shell-commands-macos-install-iphone-device-ipa | Install iPhone device .ipa. | | dwim-shell-commands-macos-make-finder-alias | Make macOS Finder alias. | | dwim-shell-commands-macos-open-with | Open file(s) with specific external app. | | dwim-shell-commands-macos-open-with-firefox | Open file(s) in Firefox. | | dwim-shell-commands-macos-open-with-safari | Open file(s) in Safari. | | dwim-shell-commands-macos-reveal-in-finder | Reveal selected files in macOS Finder. | | dwim-shell-commands-macos-screenshot-window | Select and screenshot macOS window. | | dwim-shell-commands-macos-set-default-app | Set default app for file(s). | | dwim-shell-commands-macos-share | Share selected files from macOS. | | dwim-shell-commands-macos-start-recording-window | Select and start recording a macOS window. | | dwim-shell-commands-macos-toggle-bluetooth-device-connection | Toggle Bluetooth device connection. | | dwim-shell-commands-macos-toggle-dark-mode | Toggle macOS dark mode. | | dwim-shell-commands-macos-toggle-display-rotation | Rotate display. | | dwim-shell-commands-macos-version-and-hardware-overview-info | View macOS version and hardware overview info. | | dwim-shell-commands-make-swift-package-executable | Create a swift package executable | | dwim-shell-commands-make-swift-package-library | Create a swift package library | | dwim-shell-commands-make-transparent-png | Create a transparent png. | | dwim-shell-commands-move-to-desktop | Move file to ~/Desktop. | | dwim-shell-commands-move-to-downloads | Move file to ~/Downloads. | | dwim-shell-commands-ocr-text-from-image | Extract text from image via tesseract. | | dwim-shell-commands-open-clipboard-url | Open clipboard URL. Offer to stream if possible. | | dwim-shell-commands-open-externally | Open file(s) externally. | | dwim-shell-commands-optimize-gif | Convert all marked videos to optimized gif(s). | | dwim-shell-commands-pass-git-pull | Pass git pull. | | dwim-shell-commands-pdf-password-protect | Add a password to pdf(s). | | dwim-shell-commands-pdf-to-txt | Convert pdf to txt. | | dwim-shell-commands-ping-google | Ping google.com. | | dwim-shell-commands-rename-all | Rename all marked file(s). | | dwim-shell-commands-reorient-image | Reorient images. | | dwim-shell-commands-resize-gif | Resize marked gif(s). | | dwim-shell-commands-resize-image | Resize marked image(s). | | dwim-shell-commands-resize-video | Resize marked images. | | dwim-shell-commands-sha-256-hash-file-at-clipboard-url | Download file at clipboard URL and generate SHA-256 hash. | | dwim-shell-commands-speed-up-gif | Speeds up gif(s). | | dwim-shell-commands-speed-up-video | Speed up video(s). | | dwim-shell-commands-stream-clipboard-url | Stream clipboard URL using mpv. | | dwim-shell-commands-svg-to-png | Convert all marked svg(s) to png(s). | | dwim-shell-commands-unzip | Unzip all marked archives (of any kind) using
atool'. | | dwim-shell-commands-upload-to-0x0 | Upload the marked files to 0x0.st | | dwim-shell-commands-video-to-gif | Convert all marked videos to gif(s). | | dwim-shell-commands-video-to-hevc-mkv | Convert all marked videos to hevc mkv. | | dwim-shell-commands-video-to-mp3 | Drop audio from all marked videos. | | dwim-shell-commands-video-to-optimized-gif | Convert all marked videos to optimized gif(s). | | dwim-shell-commands-video-to-thumbnail | Generate a thumbnail for marked video(s). | | dwim-shell-commands-video-to-webp | Convert all marked videos to webp(s). | | dwim-shell-commands-video-trim-beginning | Drop audio from all marked videos. | | dwim-shell-commands-video-trim-end | Drop audio from all marked videos. | | dwim-shell-commands-zip | Zip all marked files into archive.zip. | | dwim-shell-commands-zip-password-protect | Protect/encrypt zip file(s) with password. |- Evaluating elisp functions
This can be done with either of the following:
#+begin_src emacs-lisp :lexical no emacs --quick --batch --eval '(message "<
>")' #+end_src #+begin_src emacs-lisp :lexical no emacsclient --eval '(message "<
>")' #+end_src - Support this work
👉 [[https://github.com/sponsors/xenodium][Support my work via GitHub Sponsors]]