コマンド履歴の達人を目指してみる
自分も知らなかった...。まだまだ知らないことって、いっぱいある。
それにしてもコマンド履歴というのは、自分はよく使う。直接入力するより、過去の履歴を探し出して、必要な修正してからコマンド実行するパターンが多い。長〜いファイルパスやオプション指定のあるコマンドなんて、ほとんどがコマンド履歴の修正と実行である。コマンド履歴が使えるからこそ、快適なコマンド操作ができると思っている(自分の場合)。
コマンド履歴は質実剛健なコマンドの世界に、選択して実行するというGUI的なゆるい操作感を与えてくれる。コマンドを忘れる心配や、スペルを間違う心配から、救ってくれる。それほどまでにコマンド履歴に頼りきっている状況なのに、今日も上下の矢印キーを激しく連打するしかない自分は、一体どうしたら良いものか?
もっと効率的に目的のコマンド履歴に辿り着く技が必要である。
コマンド履歴の仕組み
- bashは、実行したコマンドを履歴として記録しておく。
- そのコマンド履歴は ~/.bash_history というファイルに保存される。(デフォルトの設定では)
- 但し、コマンドを実行する度に ~/.bash_history に書き込まれる訳ではない。
- bashが起動中は、bashごとに用意されるコマンド履歴のキャッシュ(一時的な保存場所)に保存される。
- bashが終了する時、コマンド履歴のキャッシュの内容が ~/.bash_history に書き込まれる。
- つまり、コマンド履歴を2段階に分けて保存するという、ちょっとややこしい仕組みなのだ。
しかし、この仕組みのおかげで、ちょっとした恩恵もある。
- 今やターミナルはタブ管理があたり前になり、通常いくつものタブを開いて作業している。(ウィンドウも複数開いている)
- いくつものタブが開いているということは、いくつものbashが起動している状態でもある。
- もし、それぞれのbashがコマンド実行毎に共通の ~/.bash_history に書き込んでしまうと、すべてのbashの履歴がごちゃ混ぜになってしまう...。
- おそらく、とっても扱いづらいコマンド履歴になること必至。
- 一方、それぞれのbashが起動中は個別のキャッシュに記録しておけば、bashごとにコマンド履歴は区別される。
- そして、bashが終了する時にまとめて ~/.bash_history に書き込むことで、タブごとにグループ化されたコマンド履歴となるのだ。
- その後、新しいタブを開く時は、bashは ~/.bash_history のコマンド履歴をキャッシュに読み込む。
- つまり、新しいタブは、既存のタブ以外のすべてのコマンド履歴にアクセスできるのだ。
- 別の言い方をすれば、過去に閉じられたタブのすべてのコマンド履歴にアクセスできるのだ。
- そして閉じる時は、自らのタブで新たに実行されたコマンドの履歴のみ(差分のみ)を ~/.bash_history に書き込む。
- その時 ~/.bash_history には書き込まれるけど、既存のタブのコマンド履歴のキャッシュには影響しない。
- bashは起動時のみ、~/.bash_history から、コマンド履歴のキャッシュへ、履歴をコピーするだけである。
- ~/.bash_historyの内容が、リアルタイムにbashごとのコマンド履歴のキャッシュに反映される訳ではない。
- そのようにして記録されるコマンド履歴は、上下の矢印キーで1履歴ごとに移動して、コマンド行で確認できるようになっている。
- control-P・NでもOK。
- コマンド行に表示されたコマンド履歴は、そのまま実行することもできるし、必要な修正をしてから実行することもできる。
履歴を確認するコマンド
.bash_historyについて
特別なコマンドは用意されていないので、既存のコマンドで覗いてみる。
$ cat ~/.bash_history
- 最新の10件を見たい。
$ cat ~/.bash_history|tail
historyコマンドの底力
- ところでhistoryコマンドが出力する、コマンド履歴左側の番号は、飾りじゃない!
$ history 10 462 cd ~/Document 463 > じじい.txt 464 > シンガポール.txt 465 > ノーライフキング.txt 466 echo じじい シンガポール ノーライフキング > spotlight_test.txt 467 man mdutil 468 mdutil -i off /Volumes/名称未設定\ 10 469 mdutil -i on /Volumes/名称未設定\ 10 470 rm -f /Volumes/名称未設定\ 10/Spotlight.txt 471 mdutil -i off /Volumes/名称未設定\ 10
- historyコマンドは履歴の閲覧だけでなく、コマンド履歴の実行もできる。
- 以下のように!に続けて番号を指定することで、そのコマンド履歴が実行されるのだ。
$ !467
man mdutil
素晴らしい!
- ちなみに、!!で直前のコマンドが実行される。
$ echo "履歴のテスト" 履歴のテスト $ !! echo "履歴のテスト" 履歴のテスト
環境設定
- コマンド履歴を記録する時の便利な設定がいろいろある。
- 以下の変数を設定することで、コマンド履歴をコントロールできる。
変数名 | 設定例とデフォルト値 | 意味 |
---|---|---|
HISTFILE | HISTFILE=~/.bash_history(デフォルト値) | 履歴ファイルの保存場所は~/.bash_history |
HISTSIZE | HISTSIZE=500(デフォルト値) | 使用中のbashの履歴キャッシュに最大500件保持する |
HISTFILESIZE | HISTFILESIZE=500(デフォルト値) | 履歴ファイルに最大500件保持する |
HISTCONTROL | HISTCONTROL=ignoredups HISTCONTROL=ignorespace HISTCONTROL=ignoreboth |
同じコマンドが連続する場合は1回だけ記録する コマンドの頭にスペースを付けて実行すると記録しない ignoredupsとignorespace上記2つどちらも設定する |
HISTIGNORE | HISTIGNORE=ls:history HISTIGNORE=?:??:???:exit |
lsとhistoryは、記録しない。(:は設定の区切り) 1〜3文字までのコマンドとexitは、記録しない。(?は何らかの1文字、*は0文字以上の何か) |
HISTTIMEFORMAT | HISTTIMEFORMAT='%Y/%m/%d %H:%M:%S ' HISTTIMEFORMAT='%y/%m/%d ' |
コマンド履歴に日時(例: 2013/10/26 12:34:56)を付加する コマンド履歴に日付(例: 13/10/26)を付加する |
HISTCMD | この変数には設定できない。読み取りのみ。 | 次にコマンドを実行した際に付く履歴番号 |
- ちなみに、HISTTIMEFORMATを設定すると便利そうなんだけど、履歴ファイルの中にUNIX時間を表現する数値が含まれることになる。
$ history 1 echo tab1.1 2 echo tab1.3 3 echo tab2.2 4 echo tab2.4 5 history $ HISTTIMEFORMAT="%Y/%m/%d %H:%M:%S " $ history 1 2013/10/26 14:47:05 echo tab1.1 2 2013/10/26 14:47:05 echo tab1.3 3 2013/10/26 14:47:05 echo tab2.2 4 2013/10/26 14:47:05 echo tab2.4 5 2013/10/26 14:47:05 history ...中略... $ cat .bash_history #1382736410 echo tab1.1 #1382764900 echo tab1.3 #1382764912 echo tab2.2 #1382764916 echo tab2.4 #1382764927 history ...中略...
なるほど、なるほど。
- という訳で、保持する履歴数を5000に変更してみた。
- 自分にとっては、デフォルトの500じゃ少ない。
- 試行錯誤して何度も実行していると、あっという間に500件が同じようなコマンドになっていたので。
- それから、繰り返しの無駄を省くためにHISTCONTROLも設定してみた。
- あと、historyのみのコマンドも記録しないことにした。
HISTSIZE=5000 # 現在使用中のbashが保持する履歴数 HISTFILESIZE=5000 # 履歴ファイルに保存される履歴数 # HISTCONTROL=ignoredups # 同じコマンドが連続する場合は1回だけ記録する # HISTCONTROL=ignorespace # コマンドの頭にスペースを付けて実行すると記録しない HISTCONTROL=ignoreboth # ignoredupsとignorespaceどちらも設定する HISTIGNORE=history # historyは記録しない。
コマンド履歴の眺め方いろいろ
画面にそのまま
$ history ...中略... 588 git commmit -m 'Fix install method.' 589 git commit -m 'Fix install method.' 590 git log 591 git tree 592 git push 593 git diff 594 git add . 595 git commit -m 'Fix install method.' 596 git push 597 history 10
- しかし、履歴件数500以上が一気に表示されてしまうのは、ちょっと気が引ける。
- これまでのコマンド出力の見通しも最悪になる...。
lessでページコントロール
- そんな時は、パイプでlessコマンドに渡せば、既存の画面出力に影響を与えずに閲覧できる。
$ history|less
- 移動
1行上・下へ移動 | ↑・↓矢印キー |
1ページ上・下へ移動 | w・z または b・スペース |
ファイルの先頭・末尾へ移動 | g・G または <・> |
- 検索
先頭・末尾方向へ語句を検索 | ?語句・/語句 |
次・前の語句に移動する | n・N |
- 終了
lessを終了する | q |
- ヘルプ
キー操作のマニュアルを表示する | h |
キー操作のマニュアルを終了する | q |
viで見る
- viに慣れている人にとっては、viで見たくなるかもしれない。(あるはemacs)
$ history|vi
- viのポテンシャルは底知れない...。
- ここではとても語り尽くせない。
- 使い方は、以前に調べた日記で。
コンソールで見る
- openコマンドは強力である。
- 結局、-aオプションで好みのアプリを指定すれば、何でも使えるということだ。
- ならば、コンソール.appで開いてみる。
$ history|open -f -a console
開けた!
- コンソール.appの素晴らしいところは、検索語句を含む行しか表示しないというフィルタ機能を持っていること。
- スペースで区切れば、二つ以上の語句を含むAND検索もできる。
履歴と修正のキー操作
目指す履歴を見つけたら、きっと修正して、実行したくなる。履歴の達人になるなら、bash環境でテキストを自由に編集したい。
履歴
履歴の先頭・末尾へジャンプ | esc <・esc > |
履歴を前・後に移動する | ↑・↓ |
履歴の検索 | control-r |
- control-rに続けて、検索語句を入力していくことで、その語句を含む一番最近の履歴を表示する。
- 検索文字が増えるほどに、絞り込まれていく。(表示されるのは一番最近の履歴のみだが)
- returnキーで、そのまま実行。
- →キーで、その履歴にジャンプ。
- その履歴を起点に↑・↓で前後に移動できる。
- 目的の履歴を見つけたら、修正して、実行するのだ。
コマンドを自由に編集する
- カーソル移動
1文字ずつ移動 | ←・→ |
単語単位で移動 | option-←・→ |
行頭・行末へ移動 | control-a・e |
- 削除
左・右方向へ1文字削除 | control-h・d |
左・右方向へ1単語削除(次の記号まで) | esc control-h・esc d |
カーソル位置から行頭・行末まで削除 | control-u・k |
カーソル位置から手前のスペースまで削除 | control-w |
- 操作の取消し
操作を1つ戻す(undo) | control-−(controlとマイナス)あるいは control-x control-u |
削除した文字をペースト | control-y |
パイプの連続技
ひとつ一つのコマンドは単機能なのだけど、それらをパイプで接続することで、簡潔な表現で、高度な情報処理が素早くできる。コマンドを使っていて、いつも感動するところ。
- mdfindを含む履歴だけ抽出する。
$ history|grep mdfind|less
- 履歴番号を外したい。
$ history|awk '{ $1=""; print $0 }'|less
- アルファベット順に並べ替える。
$ history|awk '{ $1=""; print $0 }'|sort|less
- 重複するコマンドは排除する。
$ history|awk '{ $1=""; print $0 }'|sort|uniq|wc -l 503 $ history|awk '{ $1=""; print $0 }'|sort|wc -l 645
- よく使うコマンドのベスト10。
$ history|awk '{ $1=""; print $0 }'|sort|uniq -c|sort -r|head 10 mdfind_all ノーライフキング 8 echo ノーライフキング|xargs -I{} mdfind "* == {}* || kMDItemTextContent == {}*" -onlyin ~/Documents 7 mdfind "* == ノーライフキング* || kMDItemTextContent == ノーライフキング*" -onlyin ~/Documents 6 echo シンガポール|xargs -I{} mdfind "* == {}* || kMDItemTextContent == {}*" -onlyin ~/Documents 5 mdfind_all シンガポール -onlyin ~/Documents 5 mdfind '* == "シンガポール*"c || kMDItemTextContent == "シンガポール*"'c | less 4 mdfind '* == "spotlight*"cdw' 4 mdfind '* == "spotlight*" cdw' 4 mdfind "* == シンガポール* || kMDItemTextContent == シンガポール*" -onlyin ~/Documents 4 mdfind "* == シンガポール* cdw"
- よく使うコマンドのベスト10。(引数を除いた先頭のコマンドのみで集計)
$ history|awk '{ print $2 }'|sort|uniq -c|sort -r|head 308 mdfind 90 echo 25 mdfind_all 24 sudo 24 history 14 ls 14 history|grep 14 git 13 open 12 man
- よく使うコマンドのベスト10。(おっと、sudoは除外したい、引数を除いた先頭のコマンドのみで集計)
$ history|sed -e 's/sudo //g'|awk '{ print $2 }'|sort|uniq -c|sort -r|head 308 mdfind 90 echo 25 mdfind_all 24 history 22 ls 15 rm 14 history|grep 14 git 13 open 13 defaults
あ〜、最近はMountain LionのSpotlightで試行錯誤してたから、その傾向が顕著に出てる...。
mdfind 308回てっ、どんだけ検索してたんだ...。でも、ほとんどがコマンド履歴から実行してるはず。間違いない。