Skip to content

Instantly share code, notes, and snippets.

@amno1
Last active November 4, 2023 19:09
Show Gist options
  • Save amno1/ee649f9e3c11566678d67125ef730444 to your computer and use it in GitHub Desktop.
Save amno1/ee649f9e3c11566678d67125ef730444 to your computer and use it in GitHub Desktop.
Use Bash aliases in interactive shell-comands
;;; shell-command-with-aliases.el --- Use Bash aliases in interactive shell-comands -*- lexical-binding: t; -*-
;; Copyright (C) 2023 Arthur Miller
;; Author: Arthur Miller <[email protected]>
;; Keywords:
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; Bind shell-command-with-aliases to M-! as the replacement for shell-command
;;; Code:
(defgroup shell-command nil
"Shell-command that understands Bash aliases"
:group 'tools)
(defvar bash-alias-table (make-hash-table :test 'equal)
"Table containg names and values for Bash aliases.")
(defvar bash-alias-file
(expand-file-name "etc/bash-alias-table" user-emacs-directory)
"Serialize Bash aliases to this file.")
(defun bash-aliases ()
"Obtain list of bash aliases for the current user."
(interactive)
(let ((shell-command-switch "-ic") beg)
(with-temp-buffer
(shell-command "alias" t)
(goto-char 1)
(switch-to-buffer (current-buffer))
(while (search-forward "alias" nil t)
(setq beg (point))
(search-forward "=" (line-end-position)) ;; this has to succeed
(replace-match " ")
(let ((name (string-trim (buffer-substring-no-properties
beg (1- (point)))))
(beg (1+ (point))))
(while (and (char-after)
(/= (char-before) ?\')
(/= (char-before) ?\"))
(forward-char))
(goto-char (line-end-position))
(while (and (char-before)
(/= (char-before) ?\')
(/= (char-before) ?\"))
(backward-char))
(puthash name (buffer-substring-no-properties beg (1- (point)))
bash-alias-table)))
(erase-buffer)
(prin1 bash-alias-table (current-buffer))
(write-region (point-min) (point-max) bash-alias-file))))
(defun bash-command-from-alias (cmd)
"Convert a command in CMD with alias into real command name."
(with-temp-buffer
(insert cmd)
(goto-char 1)
(let ((alias (gethash (current-word) bash-alias-table)))
(if (not alias)
cmd
(delete-region 1 (length cmd))
(insert alias)
(buffer-substring-no-properties (point-min) (point-max))))))
;;;###autoload
(defun async-shell-command-with-aliases ()
"Like `shell-command' but understands Bash aliases."
(interactive)
(let ((args (eval (cadr (interactive-form 'shell-command)))))
(apply #'async-shell-command (bash-command-from-alias (pop args)) args)))
;;;###autoload
(defun shell-command-with-aliases ()
"Like `shell-command' but understands Bash aliases."
(interactive)
(let ((args (eval (cadr (interactive-form 'shell-command)))))
(apply #'shell-command (bash-command-from-alias (pop args)) args)))
(defvar bash-alias-mode-map
(let ((map (make-sparse-keymap)))
(define-key map [remap shell-command] #'shell-command-with-aliases)
(define-key map [remap async-shell-command] #'async-shell-command-with-aliases)
map))
;;;###autoload
(define-minor-mode bash-alias-mode
"Enable Bash aliases in shell-command and async-shell-command"
:global t :lighter " alias")
(progn ;; init the table
(unless (file-exists-p bash-alias-file)
(bash-aliases))
(with-temp-buffer
(insert-file-contents bash-alias-file)
(setq bash-alias-table (read (current-buffer)))))
(provide 'shell-command-with-aliases)
;;; shell-command-with-aliases.el ends here
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment