index all rss mastodon twitter github linkedin email
Álvaro Ramírez

Álvaro Ramírez

10 September 2023 SHA-256 hash from URL, the easy way

From time to time, I need to generate a SHA-256 hash from a file hosted on some server. For me, this flow typically goes something along the lines of:

  • Copy the file URL from browser.
  • Drop to Emacs eshell.
  • Change current directory.
  • Type "curl -o file"
  • Paste the file URL.
  • Run curl command.
  • Type "shasum -a 256 file".
  • Run shasum command.
  • Copy the generated hash.
  • Maybe delete the downloaded file?

We can maybe shave some steps off by downloading directly from the browser, though that may also bring additional clicks and navigating to a download location.

Amongst the steps, shasum is the star player, and its output can be seen below.

shasum -a 256 path/to/downloaded/file
3da351027e5b1495c7c7fe4abbf8d7ac9625da3604be5a35c9a9cbb92f6f955a  path/to/downloaded/file

Not a huge deal. One can copy the hash from the output, but why go through multiple small manual steps when I know I can get Emacs to simplify the lot? I've expedited a similar flow in the past when cloning git repos. Let's simplify again so hashing a hosted file boils down to:

  • Copy the file URL from browser.
  • Run an Emacs interactive command.

This is where I pull out dwim-shell-command (a little package I wrote) and glue the lot to get an expedited experience.

hash.gif

There isn't much to the function other than glueing a little elisp and a shell script via dwim-shell-command for some buffer/error handling.

(defun dwim-shell-commands-sha-256-hash-file-at-clipboard-url ()
  "Download file at clipboard URL and generate SHA-256 hash."
  (interactive)
  (let ((url (current-kill 0)))
    (unless (string-match-p "^http[s]?://" url)
      (user-error "No URL in clipboard"))
    (dwim-shell-command-on-marked-files
     "Generate SHA-256 hash from clipboard URL."
     (format
      "temp_file=$(mktemp)
       function cleanup {
         rm -f $temp_file
       }
       trap cleanup EXIT
       curl --no-progress-meter --location --fail --output $temp_file %s || exit 1
       shasum -a 256 $temp_file | awk '{print $1}'"
      (shell-quote-argument url))
     :utils '("curl" "shasum")
     :on-completion
     (lambda (buffer process)
       (if-let ((success (= (process-exit-status process) 0))
                (hash (with-current-buffer buffer
                        (string-trim (buffer-string)))))
           (progn
             (kill-buffer buffer)
             (kill-new hash)
             (message "Copied %s to clipboard"
                      (propertize hash 'face 'font-lock-string-face)))
         (switch-to-buffer buffer))))))

dwim-shell-commands-sha-256-hash-file-at-clipboard-url is now in dwim-shell-commands.el, the optional counterpart in dwim-shell-command.

UPDATE

There's better way. Thanks to Philip Kaludercic for suggesting curl -s example.com | sha256sum - | cut -d " " -f1 and Sacha Chua who pinged me about it.

Also note I'm now relying on the <<cb>> template, since dwim-shell-command replaces it with the clipboard/kill ring.

(defun dwim-shell-commands-sha-256-hash-file-at-clipboard-url ()
  "Download file at clipboard URL and generate SHA-256 hash."
  (interactive)
  (unless (string-match-p "^http[s]?://" (current-kill 0))
    (user-error "No URL in clipboard"))
  (dwim-shell-command-on-marked-files
   "Generate SHA-256 hash from clipboard URL."
   "curl -s '<<cb>>' | sha256sum - | cut -d ' ' -f1"
   :utils '("curl" "sha256sum")
   :on-completion
   (lambda (buffer process)
     (if-let ((success (= (process-exit-status process) 0))
              (hash (with-current-buffer buffer
                      (string-trim (buffer-string)))))
         (progn
           (kill-buffer buffer)
           (kill-new hash)
           (message "Copied %s to clipboard"
                    (propertize hash 'face 'font-lock-string-face)))
       (switch-to-buffer buffer)))))