bash tips

きちんとまとめられた情報が少ないのでメモ。

bashの機能はemacsと共通する部分が多く、使われている用語やキーバインドも類似している。 以下で述べるキーバインドについて、C-xはCtrl+x、M-xESC xまたはC-[ xを表す。

kill-ringを活用する

bashにはkill-ringと呼ばれる仕組みがあり、削除(cut)した文字列を再度貼り付ける(yank)ことができる。

たとえば、C-uC-a C-kでもよい)を押すと現在入力しているコマンドラインがすべてcutされ、kill-ringに追加される。 一通り作業した後でC-yを押すと、C-uでcutしておいたコマンドラインを貼り直すことができる。

$ du --[C-u]
=> $ 

$ man du

$ [C-y]
=> $ du --

$ du --max-depth=1
210104  ./tmp
4       ./.cache
12      ./.ssh
640     ./dotfiles
28      ./.emacs.d
210844  .

kill-ringをさらに遡るには、C-yに続けてM-yを繰り返し押せばよい。

なお、C-wを押すと直前のスペースまでcutすることができる。

ヒストリにあるコマンドを再実行する

!100のように入力することで、ヒストリにある100番目のコマンドを実行できる。

$ history | grep clone
  257  git clone http://git.savannah.gnu.org/r/bash.git
  419  git clone git://github.com/SimonWilkinson/openssh.git
  880  history | grep clone

$ !257

$ git clone http://git.savannah.gnu.org/r/bash.git

~/.bashrcなどでshopt -s histverifyしておけば、いきなり実行する代わりに展開結果を表示し確認することができる。

直前のコマンドをsudoする

!!は直前に実行したコマンドライン全体を表す。 したがって、sudo !!と打てば直前のコマンドをsudoして実行できる。

$ apt-get update
E: Could not open lock file /var/lib/apt/lists/lock - open (13: Permission denied)
E: Unable to lock directory /var/lib/apt/lists/
E: Could not open lock file /var/lib/dpkg/lock - open (13: Permission denied)
E: Unable to lock the administration directory (/var/lib/dpkg/), are you root?

$ sudo !!

$ sudo apt-get update

こちらもshopt -s histverifyしておけば、実行する前に展開結果を確認することができる。

直前のコマンドラインを置換して実行する

!:gs/foo/bar/で直前のコマンドラインのfooをbarに置換して実行できる。

$ echo foo foo
foo foo

$ !:gs/foo/bar/

$ echo bar bar

これを行う場合は、shopt -s histverifyしておいたほうがよい。

なお、最初の一箇所のみの置換でよいなら、^foo^bar^を使うこともできる。

$ echo foo foo
foo foo

$ ^foo^bar^

$ echo bar foo

直前のコマンドの最後の引数を展開する

M-.を押せばよい。

$ mkdir newdirectory

$ cd [M-.]
=> $ cd newdirectory

!!のように!$で参照することもできる。

戻りすぎたヒストリをリセットする

C-rで戻りすぎたヒストリをリセットし、最後のヒストリから検索をやり直すにはM->を押せばよい。

コマンドをエディタで編集してから実行する

C-x C-eでエディタを開き、保存した内容を実行させることができるが、使う場面は少ない。 しいて言えば、コピーしてきたコマンドリストを実行させる場面ぐらいであろう。

exit statusを表示する

~/.bashrcに次のように書いておくことで、コマンドが失敗したときのexit statusを端末に表示することができる。 ここでは、他の出力と区別するために背景色を赤にしている。

if [[ -n "$PS1" ]]; then
    set -o pipefail
    trap 'echo -e "\e[41mexit ${PIPESTATUS[*]}\e[0m"' ERR
fi
$ grep
Usage: grep [OPTION]... PATTERN [FILE]...
Try 'grep --help' for more information.
exit 2

.bash_historyをコマンド実行ごとに更新する

~/.bashrcに次のように書いておくことで、通常シェルが終了するときに更新される.bash_historyをコマンド実行ごとに更新することができる。

if [[ -n "$PS1" ]]; then
    shopt -s histappend
    PROMPT_COMMAND='history -a'
fi

こうしておくことで、新たにシェルを起動した際、それまで別のシェルで打ち込まれていたヒストリが引き継がれるようになる。 また、同時に複数のシェルを起動している状況で、他のシェルで実行されたヒストリを手元のシェルに反映させるには次のコマンドを実行すればよい。

$ history -n

C-bで直前のディレクトリに戻る

~/.bashrcに次のように書いておくことで、C-bを押すとcd -が実行されるようにできる。

if [[ -n "$PS1" ]]; then
    bind '"\C-b":"\ercd -\n"'
fi

bind -x '"\C-b":"cd -"'としてもほぼ同じ動作となるが、この場合プロンプト文字列が更新されないため上のようにする。

地味に便利。

関連リンク