LoginSignup
41
36

More than 5 years have passed since last update.

Emacs Lispでシェルコマンドを活用する

Posted at

Emacs上からのコマンド実行方法は多くありますが、Emacs Lispの関数から使うことに焦点をあてます。

基本パターン

一番単純な使い方

pwdを実行して結果を文字列で得ます。

(defun my-pwd ()
  (interactive)
  (shell-command-to-string "pwd"))
;; (my-pwd) ; => "/Users/username\n"

M-x my-pwdもしくはM-: (my-pwd)で実行できます。
(pwd: カレントディレクトリを返すコマンド)

非同期でコマンドを実行

Mac OSXのsayコマンドでAlexに喋ってもらいます。shell-command-to-stringで同期実行した場合はAlexが喋っている間にEmacsを動かすことができません。そこで非同期実行のasync-shell-commandを使って実行中もEmacsを動かせるようにしています。

(defun my-say-hello ()
  (interactive)
  (async-shell-command "say -v Alex Hello! I am Emacs, at your service."))

選択範囲に対してコマンドを実行

選択範囲のaをbに置き換えます。perlコマンドの正規表現で置換

(defun my-replace-a-b ()
  (interactive)
  (when (region-active-p) ;; 選択範囲があるとき
    (shell-command-on-region (region-beginning) (region-end) "perl -0 -p -w -e \'s/a/b/g\'" nil t)))

Dired上のファイル/フォルダに対してコマンドを実行

カーソル位置の画像ファイルの情報を表示します。マークしたファイルも対象になります。

(defun my-dired-display-file-info ()
  (interactive)
  (let ((files (dired-get-marked-files t current-prefix-arg)))
    (dired-do-shell-command "file *" nil files)))

その他

コマンドのチェック

パスが通っていて使用可能ならコマンドのパスが返ります。

(executable-find "cmigemo") ; => "/usr/local/bin/cmigemo"

Emacs用にコマンドへのパスを通す

こちらを参考に
:link: PATH の設定

実践サンプル

Mac OS X向けのコマンドが多めです。

選択範囲を指定したタグで囲む

選択範囲をミニバッファに入力したタグ名で囲みます。

(defun my-wrap-lines-with-html-tag (tag)
  (interactive "sTag: ")
  (if (region-active-p)
      (shell-command-on-region
       (region-beginning) (region-end)
       (concat "perl -0 -p -w -e \'"
               "s/^([^\\S\\r\\n]*)(\\S.*?)[^\\S\\r\\n]*$/$1<"
               tag ">$2<\\/" tag ">/gm\'")
       nil t)))

カレントディレクトリ以下の特定ファイルを削除

カレントディレクトリ以下の.DS_Storeファイルを削除します。

(defun my-delete-DS_Store-under-current-directory-recursively ()
  (interactive)
  (shell-command "find . -name '*.DS_Store' -type f -delete")
  ;; Diredを開いていたらバッファを更新
  (if (eq major-mode 'dired-mode)
      (revert-buffer)))

Diredからsqliteファイルの内容を閲覧

(defun my-dired-sqlite ()
  (interactive)
  (let ((file (dired-get-filename))
        (buffer (format "SQLite-%s" (random 100000))))
    (async-shell-command (format "sqlite3 '%s'" file) buffer)
    (switch-to-buffer buffer)
    (insert ".header on")
    (execute-kbd-macro (kbd "RET")) ;; returnキー実行
    (insert ".mode column")
    (execute-kbd-macro (kbd "RET"))
    (insert ".tables")
    (execute-kbd-macro (kbd "RET"))))

Diredでマークしたファイルの特殊属性を除去

(defun my-dired-delete-mac-atmark-permission ()
  (interactive)
  (let ((files (dired-get-marked-files t current-prefix-arg)))
    (mapc (lambda (file)
            ;; マークしたファイルそれぞれに対して実行
            (dired-do-shell-command "xattr -c *" nil (list file)))
          files)
    (dired-unmark-all-marks) ;; マーク解除
    (revert-buffer) ;; バッファ更新
    ))

Diredでマークしたファイルを別アプリに送る

Image Optimアプリへ送り、画像の容量を落とします。

(defun my-dired-image-optim ()
  (interactive)
  (let ((files (dired-get-marked-files t current-prefix-arg)))
    (mapc (lambda (file)
            (cond ((string-match ".*\\.\\(png\\|jpe?g\\|gif\\)$" file)
                   ;; 指定した種類の画像ファイルの場合
                   (dired-do-shell-command "open -a ImageOptim.app *" nil (list file)))))
          files)
    (dired-unmark-all-marks)
    (revert-buffer)))

Diredでマークしたファイルを入力値を使って処理

実行後にミニバッファに値を入力します。その値で画像のリサイズを行います。
(sips: 画像をリサイズする Mac OS X 標準コマンド)

(defun my-dired-image-width-resize-with-sips (width)
  (interactive "sWidth: ")
  (let ((files (dired-get-marked-files t current-prefix-arg)))
    (mapc (lambda (file)
            (cond ((string-match ".*\\.\\(png\\|jpe?g\\|gif\\)$" file)
                   (dired-do-shell-command
                    (concat "sips --resampleWidth " width " *")
                    nil (list file)))))
          files)
    (dired-unmark-all-marks)
    (revert-buffer)))

選択範囲を任意の正規表現で置換

私のお気に入りのコードです。
M-x my-perl-replace-commandのあとに例えばs/[\n\r]//gとすると、選択範囲の改行を削除します。
他によく使うのが、s/\t/ /gでタブ文字を空白文字2つに置換しています。標準の変換関数より高速です。

;; 履歴用
(defvar my-perl-replace-history nil
  "History for the `my-perl-replace-command'.")
(defun my-perl-replace-command (command)
  "Utilize Perl regular expression to replace string in the current buffer."
  (interactive (list (read-shell-command "s[perl regexp] : "
                                         ""
                                         'my-perl-replace-history)))
  (when (string-match "'" command)
    (setq command (replace-regexp-in-string "'" "\\\\x27" command)))
  (if (region-active-p)
      (shell-command-on-region
       (region-beginning) (region-end) (concat "perl -0 -p -w -e \'" command "\'") nil t)
    (let ((po (point))
          (win (window-start)))
      (mark-whole-buffer)
      (shell-command-on-region
       (region-beginning) (region-end)
       (concat "perl -0 -p -w -e \'" command "\'") nil t)
      (goto-char po)
      (set-window-start (selected-window) win))))
41
36
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
41
36