8823


Bash からは Ctrl-x Ctrl-e の操作で `$VISUAL`, `$EDITOR`
にあるプログラムを呼んで，コマンドの編集をすることができます． `man bash` より

[code]

    edit-and-execute-command (C-xC-e)
          Invoke an editor on the current command line, and execute the result
          as shell commands.  Bash attempts to invoke  $VISUAL,  $EDITOR,  and
          emacs as the editor, in that order.
    
[/code]

ところが，僕の理解が正しければ `$VISUAL` や `$EDITOR` は `sudoedit`
を始め他のプログラム/コマンドの挙動にも影響を及ぼしてしまいます．これを回避して， `C-x C-e`
で起動されるプログラムのみを別に設定することはできるでしょうか？ 今使っているのは Bash の 4.3.11 です．

  1. edit-and-execute-command 自体のカスタマイズ
  2. edit-and-execute-command をエミュレートしたような別のコマンド(編集 -> chmod -> 実行 -> rm)を作ってショートカットを登録
  3. シェルスクリプトを書いて `$EDITOR` に登録．このシェルスクリプトは edit-and-execute-command 経由で呼ばれたかどうかを調べて，それに応じて適切なコマンドを呼んでくる

などを考えたのですが， 1 と 3 については方法がわからず，2 については `echo $0`
の挙動がことなるなどどうもきちんと代替になるものをつくれませんでした．

* * *

EDIT :

まず1. については自前ビルドは想定していません（…がこれも有用な答えだと思います）． 2. はだいたい次のようなことを試しました（雑ですね）

[code]

    #!/bin/bash
    # edit_and_exec_bash
    tmpName="/tmp/bash-fc-`date +%s`"
    myeditorcommand $tmpName
    chmod u+x $tmpName
    $tmpName # このようなことをやっているので echo $0 は正しく動作しなかった
    rm $tmpName
    
[/code]

これを PATH の通ったところにおいて `~/.bashrc`:

[code]

    bind -x '"\C-x\C-e":edit_and_exec_bash'
    
[/code]

上記の通り一時ファイルに書き込んでそれを実行しているだけなので `$0`などはそのような値になります．

* * *

モチベーション（なぜこんなことをしたいのか）： ふだんエディタは vim を使っており， edit-and-execute-command でももちろん
vim を使いたいのですが， これで起動した vim
はほぼ確実にシェルスクリプトの一時ファイルのみを扱い，他のバッファはひとつも開かずに終了することになるため，僕が `.vimrc`
に書いている設定のほとんどは必要なく（どちらかというと `vim -u NONE -N` +
ほんのちょっと，くらいでつかいたい），逆に身軽さは重要になってきます．そこで `C-x C-e` では「この時専用のごく身軽な
vim」を使えたらなー，という発想です． Vim
側からでも色々やればそれなりにできるでしょうが，普段使いの設定ファイルの中にこういう設定を埋め込むよりは，起動側で選べたほうが嬉しいです．


CC BY-SA 3.0
2015-04-06T03:25:36.470
8808
2015-04-07T01:45:15.257
2015-04-07T01:45:15.257
2901
2901
question
bash
Bash から <C-x><C-e> で呼べるプログラム「のみ」の設定


僕がやるなら、なので正解ではありませんが。僕なら vimrc の頭の方に以下を書きます。

[code]

    if  argc() == 1 && argv(0) =~ '^/tmp/bash-fc-'
      " bash の ^X ^E の場合だけ処理...
      finish
    endif
    
[/code]


CC BY-SA 3.0
2015-04-06T03:40:31.673
8809
2015-04-06T03:40:31.673
440
8808
answer


  1. `edit-and-execute-command` 自体のカスタマイズ  
`readline` 用の関数自体をスクリプトでカスタマイズする方法はない気がします(スクリプトから直接 `readline`
用の関数を呼び出せれば良いのですが…)。

`bash` を自分でビルドするならば、`bash-4.3/bashline.c` の

[code]     #define VI_EDIT_COMMAND         "fc -e
\"${VISUAL:-${EDITOR:-vi}}\""

    #define EMACS_EDIT_COMMAND      "fc -e \"${VISUAL:-${EDITOR:-emacs}}\""
    
[/code]

という所を書き換えれば良さそうな気がします。例えば `EEEDIT` というシェル変数に専用のエディタコマンドを設定するとすれば、以下の様にするなど。

[code]     #define VI_EDIT_COMMAND         "fc -e
\"${EEEDIT:-${VISUAL:-${EDITOR:-vi}}}\""

    #define EMACS_EDIT_COMMAND      "fc -e \"${EEEDIT:-${VISUAL:-${EDITOR:-emacs}}}\""
    
[/code]

~~※実際に動作確認はしていません~~  
追記: @take88 さんが動作確認して下さった様です

  2. `edit-and-execute-command` をエミュレートしたような(以下略)  
例えば…

[code]     # bashrc

    
    EEEDIT='好きなエディタ'
    _EDITEXEC_FILE=
    _EDITEXEC_CMD=
    function my-edit-and-execute-command {
      _EDITEXEC_FILE="$(mktemp /tmp/bash-fc-XXXXXXXXX)"
      echo "$READLINE_LINE" > "$_EDITEXEC_FILE"
      READLINE_LINE= READLINE_POINT=0
      if ${EEEDIT:-${VISUAL:-${EDITOR:-emacs}}} "$_EDITEXEC_FILE"; then
        _EDITEXEC_CMD="$(< "$_EDITEXEC_FILE")"
        echo "$_EDITEXEC_CMD" >&2
        history -s -- "$_EDITEXEC_CMD"
      else
        _EDITEXEC_CMD=
      fi
      /bin/rm -f "$_EDITEXEC_FILE"
    }
    bind -x '"\C-x\C-e": my-edit-and-execute-command; eval -- "$_EDITEXEC_CMD"'
    
[/code]

一応上記のようにすれば `echo $0` などは期待通りに動くのではないかと思います。`eval`
を用いて実行対象のコマンドを一番外側で評価するのが鍵です。この様にしておけば `declare` 等の (関数内で使うと効果が変わる様な)
コマンドも正しく動作します。

但し `LINENO` などの特殊シェル変数の振る舞いは怪しいので、これでも `edit-and-execute-command`
と完全互換という訳には行きませんが。

また、細かい振る舞いについて、上記スクリプトを弄る余地はあると思います。(例えば `C-z (SIGTSTP)` で suspend した場合など?)

  3. シェルスクリプトを書いて `$EDITOR` に登録．(以下略)  
渡されたファイル名が `/tmp/bash-fc-` で始まるかどうかで判定できる様に思います。

[code]     #!/bin/bash

    
    EEEDIT="editexec用の編集コマンド"
    EDIT="通常の編集コマンド"
    if [[ -f $1 && $1 == /tmp/bash-fc-* ]]; then
      exec "$EEEDIT" "$@"
    else
      exec "$EDIT" "$@"
    fi
    
[/code]

但し `fc` (`bash` 組み込みコマンド) 使用時にも `/tmp/bash-fc-XXXXXXXXX` というファイル名になる事に注意。`fc`
で更に別の編集コマンドを使用したい時は、シェル変数 `FCEDIT` に設定して下さい。

P.S.

所で…事情などあるのかもしれませんが、bash-4.3.11 は
[shellshock](http://en.wikipedia.org/wiki/Shellshock_%28software_bug%29) の修正前の
patchlevel なので update した方がよい気がします…。


CC BY-SA 3.0
2015-04-06T10:58:52.780
8823
2015-04-06T21:14:57.223
2015-04-06T21:14:57.223
5300
5300
8808
answer


bash の(というより readline library の) keyboard macro
だけで(無理矢理にでも)実現するとしたら以下の様になるでしょう。

[code]

    export _FCEDIT=nano
    bind $'"\C-x\C-e":"\C-a\C-khistory -ps \'\C-y\'\C-mfc -e "$_FCEDIT"\C-m"'
    
[/code]

前提条件としては以下です。

  * Readline library の key binding map は emacs mode
  * `\C-a`, `\C-k`, `\C-y`, `\C-m` の binding はデフォルトのまま

ただ、これには様々な問題があります。

  * 所詮は keyboard macro なので、`history -ps ...` と `fc -e ...` という文字列が画面に残ってしまう(表示されたままになる)
  * コマンドラインに何も入力されていない状態で `\C-x\C-e` を入力すると、直前の `kill-line` の実行結果(文字列)がエディタコマンドに渡されてしまう
  * bash の history(履歴)に編集前の入力文字列や `history -ps ...`、`fc -e ...` が溜まって行くことになる
  * 対応策として `HISTIGNORE="history -ps:fc -e"` などとしてしまうと、
[code]     $ history -ps <= ここで `\C-x\C-e` を入力

    
[/code]

した場合には、文字列 `history -ps` はエディタコマンドに渡されない

ですので、まぁ、参考程度にどうぞ。


CC BY-SA 3.0
2015-04-06T12:42:21.897
8829
2015-04-06T12:42:21.897
8808
answer