👉 Support this work via GitHub Sponsors
Use dwim-shell-command-on-marked-files
to define new functions that apply command-line utilities to current buffer or dired files.
For example:
(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 '<<f>>' -pix_fmt rgb24 -r 15 '<<fne>>.gif'"
:utils "ffmpeg"))
Can be applied as:
This makes wrapping one-liners a breeze, so let’s do some more…
(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 '<<f>>' '<<fne>>.jpg'"
:utils "convert"))
(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 '<<f>>' -acodec libmp3lame '<<fne>>.mp3'"
:utils "ffmpeg"))
(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))
(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 '<<f>>' | tail -n 1 | cut -s -d',' -f2-2)\"
if [ -z \"$lat\" ]; then
echo \"no latitude\"
exit 1
fi
lon=\"$(exiftool -csv -n -gpslatitude -gpslongitude '<<f>>' | tail -n 1 | cut -s -d',' -f3-3)\"
if [ -z \"$lon\" ]; then
echo \"no longitude\"
exit 1
fi
if [[ $OSTYPE == darwin* ]]; then
open \"http://www.openstreetmap.org/?mlat=${lat}&mlon=${lon}&layers=C\"
else
xdg-open \"http://www.openstreetmap.org/?mlat=${lat}&mlon=${lon}&layers=C\"
fi"
:utils "exiftool"
:error-autofocus t
:silent-success t))
(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('<<f>>')))})
fpath = '<<fne>>.json'
with open(fpath , 'w') as f:
f.write(text)"
:shell-util "python"
:shell-args "-c"))
(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 = \"<<f>>\"
print(\"reading \\(filePath)\")
let content = try String(contentsOfFile: filePath).trimmingCharacters(in: .whitespacesAndNewlines)
let parsedCSV = content.components(separatedBy: CSVWritingOptions().newline).map{
$0.components(separatedBy: \",\")
}
let jsonEncoder = JSONEncoder()
let jsonData = try jsonEncoder.encode([\"value\": parsedCSV])
let json = String(data: jsonData, encoding: String.Encoding.utf8)
let outURL = URL(fileURLWithPath:\"<<fne>>.json\")
try json!.write(to: outURL, atomically: true, encoding: String.Encoding.utf8)
print(\"wrote \\(outURL)\")"
:shell-pipe "swift -"))
While you may want to build your own command toolbox over time, I’ve also 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 issue or just ping me (Mastodon / Twitter / Reddit / Email).
Run M-x dwim-shell-command
to execute disposable DWIM shell commands
- Asynchronously.
- Using noweb templates.
- Automatically injecting files (from dired or other buffers) or kill ring.
- Managing buffer focus with heuristics.
- Showing progress bar.
- Quick buffer exit.
- More reusable history.
dwim-shell-command
determines which file(s) you want the command to operate on.
If visiting a dired buffer, draw the marked file(s).
If visiting a buffer with an associated file, use that.
Operate on drawn files using either the following:
<<f>>
(file path)<<fne>>
(file path without extension)<<e>>
(extension)<<td>>
(generate a temporary directory)<<*>>
(all files joined)<<cb>>
(clipboard)
For example:
With drawn files path/to/image1.png
and path/to/image2.png
convert <<f>> <<fne>>.jpg
expands to
convert path/to/image1.png path/to/image1.jpg
convert path/to/image2.png path/to/image2.jpg
while ls -lh <<*>>
expands to
ls -lh path/to/image1.png path/to/image2.png
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.
After process is finished:
If there were any files created in the default-directory
, jump to a dired buffer and move point to the new file (via dired-jump).
If no new files were created, automatically switch focus to the process buffer and display its output.
Note: You can prevent this automatic focus by prepending your command with whitespace.
” convert ‘<<f>>’ ‘<<fne>>.jpg’”
If the shell command caused any errors, offer to focus the process buffer and display its output.
Command-line utilities like 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.
Process buffers are read-only and can be quickly closed by pressing q
.
Because of templates, command history becomes automatically reusable in other contexts.
dwim-shell-command
is available on MELPA.
- Install via M-x package-install.
- Require, set edit style, and add company backend:
(require 'dwim-shell-command)
Now you’re ready to run
M-x dwim-shell-command
Alternatively, can also install via use-package, define your own commands and remap to shell-command
’s existing binding using something like:
(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 '<<f>>' -pix_fmt rgb24 -r 15 '<<fne>>.gif'"
:utils "ffmpeg")))
I’m including an optional package (dwim-shell-commands.el), with all the command line utilities I’ve brought in over time. You can load this optional package via:
(require 'dwim-shell-commands)
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…
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 in `default-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. |
This can be done with either of the following:
emacs --quick --batch --eval '(message "<<f>>")'
emacsclient --eval '(message "<<f>>")'