bashのfcコマンドを整形後にエディタで編集する

bashはshoptでcmdhistがonになっていると複数行入力されたとき一行にしてくれる。
zshの複数行編集と比較すると悲しい…このままではいられない!(笑


bashはfcなるコマンドで直前のコマンドラインをエディタで編集した後に実行する機能がある。
一方、zshの複数行編集は新規行挿入ができない。(とおもう)


じゃあ、エディタに回す前に複数行!しかも整形済みで渡してやれば編集時はインデント付き、結果はワンライナーと両方の長所がとれるのではないのか?


と、いうことで以下のいずれかを~/.bashrcに追加。

vimを使う場合

function fceditor ()  {  tt=/tmp/$$.sh; cat $1 | ( read -rd '' s; t=/tmp/$$; echo "function a(){" > $t; echo "$s" >> $t; echo -e " }\n declare -f a" >> $t; chmod +x $t; $t > /dev/null 2>&1; if [ $? = 0 ]; then a=`$t|sed '1,2d;$d;s/....//'``";"`echo "$s"|tail -1|sed 's/^.\+\(#[^#]\+\)$/\1/'`; else a="$s"; fi; rm $t; echo "$a" ) > $tt; vim $tt; sed 's/^ *//' $tt > $1; rm $tt; }
export FCEDIT=fceditor

emacsを使う場合

マシンが速くなったおかげでemacsもリアルタイムに立ち上げて大丈夫になったな。(重いって言われていたのは既に過去?)

function fceditor ()  {  tt=/tmp/$$.sh; cat $1 | ( read -rd '' s; t=/tmp/$$; echo "function a(){" > $t; echo "$s" >> $t; echo -e " }\n declare -f a" >> $t; chmod +x $t; $t > /dev/null 2>&1; if [ $? = 0 ]; then a=`$t|sed '1,2d;$d;s/....//'`";"`echo "$s"|tail -1|sed 's/^.\+\(#[^#]\+\)$/\1/'`; else a="$s"; fi; rm $t; echo "$a" ) > $tt; emacs -nw $tt; sed 's/^ *//' $tt > $1; rm $tt; }
export FCEDIT=fceditor

実行例

$ for i in {1..3}
>   do for j in {1..4}
>      do echo $i $j
> done
> done
1 1
1 2
1 3
1 4
2 1
2 2
2 3
2 4
3 1
3 2
3 3
3 4
$ fc

ここでエディタが立ち上がる。(ちゃんとshellモード)

エディタ内でセーブ&終了後

for i in {1..3};
do
for j in {1..4};
do
echo $i $j;
done;
done
1 1
1 2
1 3
1 4
2 1
2 2
2 3
2 4
3 1
3 2
3 3
3 4
$ 

ポイントは履歴に現れるものが一行に圧縮できている点だな。コピペに最高!

$ for i in {1..3}; do for j in {1..4}              ; do echo $i $j; done; done

これもfc後では以下のように補正される

$ for i in {1..3}; do for j in {1..4}; do echo $i $j; done; done

コメントが消えてしまうのが難点か最終行のコメントが残る

展開したソース

もちろん自分自身で整形したソースをコピペしてある。

function fceditor () 
{ 
    tt=/tmp/$$.sh;
    cat $1 | ( read -rd '' s;
    t=/tmp/$$;
    echo "function a(){" > $t;
    echo "$s" >> $t;
    echo -e " }\n declare -f a" >> $t;
    chmod +x $t;
    $t > /dev/null 2>&1;
    if [ $? = 0 ]; then
        a=`$t|sed '1,2d;$d;s/....//'``";"`echo "$s"|tail -1|sed 's/^.\+\(#[^#]\+\)$/\1/'`;
    else
        a="$s";
    fi;
    rm $t;
    echo "$a" ) > $tt;
    emacs -nw $tt;
    sed 's/^ *//' $tt > $1;
    rm $tt
}
追記 2009/03/11 15:20:04:

最終行コメントに対応

ヒアドキュメントが使えない。
原因はbashでcmdhistがonになっていると複数行入力されたとき一行にするアルゴリズムにある。
下記のhistoryの文字列中をみると EOFまでの文字が履歴から消えていることで確認がとれる。
bash4.0ではcmdhistで一行にすることを諦めているfcの呼び出し方を工夫しないとこの点は解決できなさそう。

$ function foo {
> cat <<EOF
> bar
> buzz
> EOF
> }
$ history | grep function | tail -1
 4025  function foo { cat <<EOF; }
$ declare -f foo
foo () 
{ 
    cat  <<EOF
bar
buzz
EOF

}

declareでは保持されているところが笑える。

あと、zshの新規行追加は C-kC-yC-yC-kでいける なんとかなるもんだなぁ。vi互換キーバインドだとすぐできるのかな?