シェル難民がeshellに漂流したついでに、 eshell tipsまとめてみた

こんばんわ、myuhe a.k.a シェルを揺蕩う旅人 です。
さて、皆さんシェルは何をお使いでしょうか?
bash? csh? zsh?
メインエディタと同じくらい意見が分かれるものに、シェルがあるかと思います。かくいう僕も、エディタについてはEmacsばかりですが、シェルに関してはいろいろと試行を繰り返してきました。
シェルに関してはEmacsと合わせて使うとなれば、Emacsの中で動かすか、外で動かすかという選択肢も出てくるので、いろいろと悩まねばなりません。
しばらく、Emacsのansi-termでzshというのが定着していたのですが、最近使いはじめたauto-fu.zshの相性があまりよろしくなく、このところはmlterm上でzshを動かしてました。
では、それで満足かというといろいろと不満もあって、mltermとEmacsの連携がうまくないとか、そもそもEmacsから出ずに渡世したいとか、いろいろあるわけです。
そんな感じで悶々としていた時にeshellの存在を思いだしました。デフォルトの状態では、とても使える代物でもありませんが、そこはEmacs、いろいろと手を加えることで格段に便利になりました。
というわけで、まだまだeshell道半ばの段階ですが、eshell周りで施した諸々をまとめておくことにします。コードは.emacsあたりに貼ってお使いください。
プロンプトの変更
何事も見た目重要です。まずはプロンプトから変えてみます。長たらしいパスとかだと格段に見にくくなるので、パスの後に改行を入れ、シェルの記号も変えてお茶目な印象に
(defun my-eshell-prompt () (concat (eshell/pwd) "n→ " )) (setq eshell-prompt-function 'my-eshell-prompt) (setq eshell-prompt-regexp "^[^#$n]*[#→] ")
こんな感じになります。
sudoの後でコマンドを補完
メインマシンがUbuntuなので、sudoを多用します。zshだとsudoの後もコマンドを補完してくれるのですが、eshellの場合、sudoの後でTABを何度叩いても反応がないので悲しくなります。
というわけで、sudoの後でもコマンドを補完できるようにしてみました。
(defun pcomplete/sudo () "Completion rules for the `sudo' command." (let ((pcomplete-help "complete after sudo")) (pcomplete-here (pcomplete-here (eshell-complete-commands-list)))))
ブックマークしているディレクトリをeshell上に展開
ネタ元はいつもお世話になっているEmacs wiki
bmkと入力した後に、ブックマークしているディレクトリのブックーマーク名を入力するとそのパスがカレントディレクトリになります。当然補完もできて快適。こういったワザはeshellならではだなーと思いました。
;; eshell/bmk - version 0.1.3 (defun pcomplete/eshell-mode/bmk () "Completion for `bmk'" (pcomplete-here (bookmark-all-names))) (defun eshell/bmk (&rest args) "Integration between EShell and bookmarks. For usage, execute without arguments." (setq args (eshell-flatten-list args)) (let ((bookmark (car args)) filename name) (cond ((eq nil args) (format "Usage: * bmk BOOKMARK to ** either change directory pointed to by BOOKMARK ** or bookmark-jump to the BOOKMARK if it is not a directory. * bmk . BOOKMARK to bookmark current directory in BOOKMARK. Completion is available.")) ((string= "." bookmark) ;; Store current path in EShell as a bookmark (if (setq name (car (cdr args))) (progn (bookmark-set name) (bookmark-set-filename name (eshell/pwd)) (format "Saved current directory in bookmark %s" name)) (error "You must enter a bookmark name"))) (t ;; Check whether an existing bookmark has been specified (if (setq filename (cdr (car (bookmark-get-bookmark-record bookmark)))) ;; If it points to a directory, change to it. (if (file-directory-p filename) (eshell/cd filename) ;; otherwise, just jump to the bookmark (bookmark-jump bookmark)) (error "%s is not a bookmark" bookmark))))))
eshellでauto-completeを使う
実は、これが結構苦戦してしまってなかなか思うように動作しません。原因は、eshellで補完に使われているpcompleteが文脈によって動作がいろいろと異なるせいで、下のコードだと、シンボルの補完とかで変な動作をします。ハックしていただける方募集中です。
(add-to-list 'ac-modes 'eshell-mode) ;;pcomplete-parse-argumentsの一部を削除しただけ (defun ac-pcomplete-parse-arguments (&optional expand-p) "Parse the command line arguments. Most completions need this info." (let ((results (funcall pcomplete-parse-arguments-function))) (when results (setq pcomplete-args (or (car results) (list "")) pcomplete-begins (or (cdr results) (list (point))) pcomplete-last (1- (length pcomplete-args)) pcomplete-index 0 pcomplete-stub (pcomplete-arg 'last)) (let ((begin (pcomplete-begin 'last))) (if (and pcomplete-cycle-completions (listp pcomplete-stub) ;?? (not pcomplete-expand-only-p)) (let* ((completions pcomplete-stub) ;?? (common-stub (car completions)) (c completions) (len (length common-stub))) (while (and c (> len 0)) (while (and (> len 0) (not (string= (substring common-stub 0 len) (substring (car c) 0 (min (length (car c)) len))))) (setq len (1- len))) (setq c (cdr c))) (setq pcomplete-stub (substring common-stub 0 len) pcomplete-autolist t) (throw 'pcomplete-completions completions)) (when expand-p (if (stringp pcomplete-stub) (if (and (listp pcomplete-stub) pcomplete-expand-only-p) ;; this is for the benefit of `pcomplete-expand' (setq pcomplete-last-completion-length (- (point) begin) pcomplete-current-completions pcomplete-stub) (error "Cannot expand argument")))) (if pcomplete-expand-only-p (throw 'pcompleted t) pcomplete-args)))))) (defun ac-pcmpl () "Return a list of completions for the current argument position." (catch 'pcomplete-completions (when (ac-pcomplete-parse-arguments pcomplete-expand-before-complete) (if (= pcomplete-index pcomplete-last) (funcall pcomplete-command-completion-function) (let ((sym (or (pcomplete-find-completion-function (funcall pcomplete-command-name-function)) pcomplete-default-completion-function))) (ignore (pcomplete-next-arg) (funcall sym))))))) (ac-define-source pcomplete '((candidates . ac-pcmpl))) (defun my-ac-eshell-mode () (setq ac-sources '(ac-source-pcomplete ;;ac-source-words-in-buffer ac-source-dictionary))) (add-hook 'eshell-mode-hook (lambda () (my-ac-eshell-mode)))
eshellはzshを越えられるか
いろいろと書きましたが、正直なところまだzshの方が使いやすいです。悲しいことにeshell関連の資料がとっても少なく、結局コード読む羽目になるため、zshを越える環境になるのはまだ先になりそうです。つか、zshすごすぎます。
ただ、潜在的にはzshに到底できないこともeshellではできます。膨大なelisp資産を使役できるというのはeshellの最大の魅力だと思います。もっと早くeshellの良さを理解できていればなーとちょっぴり後悔しています。
というわけで、これを読んでいただいた方が少しでもeshellに興味をもってもらえたのであれば、嬉しい限りです。
-
前の記事
Rソースコードへのグラフ埋め込みをサポートするinlineR.elを書いてみた 2011.02.10
-
次の記事
eshellでMerucurialのコマンドを補完するpcmpl-hg.elを書いた 2011.02.28