Skip to content

saucoide/dwim-shell-command

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

👉 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 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…

One-liners

(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))

Multi-line scripts

(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))

Pick your language

(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 -"))

Build a rich toolbox (or use mine)

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).

A shell-command, async-shell-command, and dired-do-shell-command alternative

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.

Which files

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.

noweb templates

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

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.

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).

images/showme.png

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.

Easily create utilities

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.

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.

Install

dwim-shell-command is available on MELPA.

https://melpa.org/packages/dwim-shell-command.svg

  1. Install via M-x package-install.
  2. Require, set edit style, and add company backend:
(require 'dwim-shell-command)

Now you’re ready to run

M-x dwim-shell-command

use-package

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")))

My toolbox

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…

CommandDescription
dwim-shell-commands-audio-to-mp3Convert all marked audio to mp3(s).
dwim-shell-commands-clip-round-rect-gifClip gif(s) with round rectangle.
dwim-shell-commands-clipboard-to-qrGenerate a QR code from clipboard.
dwim-shell-commands-copy-to-desktopCopy file to ~/Desktop.
dwim-shell-commands-copy-to-downloadsCopy file to ~/Downloads.
dwim-shell-commands-docx-to-pdfConvert docx(s) to pdf (via latex).
dwim-shell-commands-download-clipboard-stream-urlDownload clipboard URL.
dwim-shell-commands-drop-video-audioDrop audio from all marked videos.
dwim-shell-commands-duplicateDuplicate file.
dwim-shell-commands-epub-to-orgConvert epub(s) to org.
dwim-shell-commands-external-ipCopy external IP to kill ring.
dwim-shell-commands-files-combined-sizeGet files combined file size.
dwim-shell-commands-gif-to-videoConvert all marked gif(s) to video(s).
dwim-shell-commands-git-clone-clipboard-urlClone git URL in clipboard to `default-directory’.
dwim-shell-commands-git-clone-clipboard-url-to-downloadsClone git URL in clipboard to “~/Downloads/”.
dwim-shell-commands-git-delete-untracked-filesDelete untracked git files in `default-directory’.
dwim-shell-commands-git-list-untracked-filesList untracked git files in `default-directory’.
dwim-shell-commands-http-serve-dirHTTP serve current directory.
dwim-shell-commands-image-clear-exif-metadataClear EXIF metadata in image(s).
dwim-shell-commands-image-exif-metadataView EXIF metadata in image(s).
dwim-shell-commands-image-horizontal-flipHorizontally flip image(s).
dwim-shell-commands-image-reverse-geocode-locationReverse geocode image(s) location.
dwim-shell-commands-image-scan-codeScan any code (ie. qr, bar, etc) from image(s).
dwim-shell-commands-image-to-grayscaleConvert all marked images to grayscale.
dwim-shell-commands-image-to-icnsConvert png to icns icon.
dwim-shell-commands-image-to-jpgConvert all marked images to jpg(s).
dwim-shell-commands-image-to-pngConvert all marked images to png(s).
dwim-shell-commands-image-trim-bordersTrim image(s) border (useful for video screenshots).
dwim-shell-commands-image-vertical-flipHorizontally flip image(s).
dwim-shell-commands-image-view-location-in-mapOpen image(s) location in map/browser.
dwim-shell-commands-image-view-location-in-openstreetmapOpen image(s) location in map/browser.
dwim-shell-commands-join-as-pdfJoin all marked images as a single pdf.
dwim-shell-commands-join-images-horizontallyJoin all marked images horizontally as a single image.
dwim-shell-commands-join-images-verticallyJoin all marked images vertically as a single image.
dwim-shell-commands-kill-gpg-agentKill (thus restart) gpg agent.
dwim-shell-commands-kill-processSelect and kill process.
dwim-shell-commands-macos-abort-recording-windowStop recording a macOS window.
dwim-shell-commands-macos-add-to-photosAdd to Photos.app.
dwim-shell-commands-macos-bin-plist-to-xmlConvert binary plist to xml.
dwim-shell-commands-macos-caffeinateInvoke caffeinate to prevent mac from sleeping.
dwim-shell-commands-macos-convert-to-mp4Convert to mov to mp4
dwim-shell-commands-macos-end-recording-windowStop recording a macOS window.
dwim-shell-commands-macos-install-iphone-device-ipaInstall iPhone device .ipa.
dwim-shell-commands-macos-make-finder-aliasMake macOS Finder alias.
dwim-shell-commands-macos-open-withOpen file(s) with specific external app.
dwim-shell-commands-macos-open-with-firefoxOpen file(s) in Firefox.
dwim-shell-commands-macos-open-with-safariOpen file(s) in Safari.
dwim-shell-commands-macos-reveal-in-finderReveal selected files in macOS Finder.
dwim-shell-commands-macos-screenshot-windowSelect and screenshot macOS window.
dwim-shell-commands-macos-set-default-appSet default app for file(s).
dwim-shell-commands-macos-shareShare selected files from macOS.
dwim-shell-commands-macos-start-recording-windowSelect and start recording a macOS window.
dwim-shell-commands-macos-toggle-bluetooth-device-connectionToggle Bluetooth device connection.
dwim-shell-commands-macos-toggle-dark-modeToggle macOS dark mode.
dwim-shell-commands-macos-toggle-display-rotationRotate display.
dwim-shell-commands-macos-version-and-hardware-overview-infoView macOS version and hardware overview info.
dwim-shell-commands-make-swift-package-executableCreate a swift package executable
dwim-shell-commands-make-swift-package-libraryCreate a swift package library
dwim-shell-commands-make-transparent-pngCreate a transparent png.
dwim-shell-commands-move-to-desktopMove file to ~/Desktop.
dwim-shell-commands-move-to-downloadsMove file to ~/Downloads.
dwim-shell-commands-ocr-text-from-imageExtract text from image via tesseract.
dwim-shell-commands-open-clipboard-urlOpen clipboard URL. Offer to stream if possible.
dwim-shell-commands-open-externallyOpen file(s) externally.
dwim-shell-commands-optimize-gifConvert all marked videos to optimized gif(s).
dwim-shell-commands-pass-git-pullPass git pull.
dwim-shell-commands-pdf-password-protectAdd a password to pdf(s).
dwim-shell-commands-pdf-to-txtConvert pdf to txt.
dwim-shell-commands-ping-googlePing google.com.
dwim-shell-commands-rename-allRename all marked file(s).
dwim-shell-commands-reorient-imageReorient images.
dwim-shell-commands-resize-gifResize marked gif(s).
dwim-shell-commands-resize-imageResize marked image(s).
dwim-shell-commands-resize-videoResize marked images.
dwim-shell-commands-sha-256-hash-file-at-clipboard-urlDownload file at clipboard URL and generate SHA-256 hash.
dwim-shell-commands-speed-up-gifSpeeds up gif(s).
dwim-shell-commands-speed-up-videoSpeed up video(s).
dwim-shell-commands-stream-clipboard-urlStream clipboard URL using mpv.
dwim-shell-commands-svg-to-pngConvert all marked svg(s) to png(s).
dwim-shell-commands-unzipUnzip all marked archives (of any kind) using `atool’.
dwim-shell-commands-upload-to-0x0Upload the marked files to 0x0.st
dwim-shell-commands-video-to-gifConvert all marked videos to gif(s).
dwim-shell-commands-video-to-hevc-mkvConvert all marked videos to hevc mkv.
dwim-shell-commands-video-to-mp3Drop audio from all marked videos.
dwim-shell-commands-video-to-optimized-gifConvert all marked videos to optimized gif(s).
dwim-shell-commands-video-to-thumbnailGenerate a thumbnail for marked video(s).
dwim-shell-commands-video-to-webpConvert all marked videos to webp(s).
dwim-shell-commands-video-trim-beginningDrop audio from all marked videos.
dwim-shell-commands-video-trim-endDrop audio from all marked videos.
dwim-shell-commands-zipZip all marked files into archive.zip.
dwim-shell-commands-zip-password-protectProtect/encrypt zip file(s) with password.

Evaluating elisp functions

This can be done with either of the following:

emacs --quick --batch --eval '(message "<<f>>")'
emacsclient --eval '(message "<<f>>")'

Support this work

👉 Support my work via GitHub Sponsors

About

Emacs shell commands with DWIM behaviour

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Emacs Lisp 100.0%