今年も終わりが近づいてきたので、自分がよく使うコマンドをランキング形式にして振り返ることができたら面白そうと思ったのでやってみました。
結論
各種コマンドの使い方がBashかZshか、GNU系のOS(Linux)かBSD系のOS(Macなど)かで微妙に変わってきます。
実行コマンド例は以下のとおりです。
累計でベスト10を出してみる
# Bash x BSDの場合 (※ $HISTTIMEFORMAT='%F %T')
history \
| awk '{ for (i = 4; i < NF; i++) { printf("%s ", $i) } print $NF }' \
| sed 's/|/\'$'\n/g' \
| awk '{ print $1 }' \
| sort \
| uniq -c \
| sort -r \
| head
# Zsh x GNUの場合
history 1 \
| awk '{ for (i = 2; i < NF; i++) { printf("%s ", $i) } print $NF }' \
| sed 's/|/\n/g' \
| awk '{ print $1 }' \
| sort \
| uniq -c \
| sort -r \
| head
Bashの場合history
に引数を指定する必要はなく、2行目のawk
のiは4から始まります。(環境変数HISTTIMEFORMATが%F %T
のとき)
Zshの場合history
の引数に1が指定され、2行目のawk
のiは2から始まります。
また、awk
がBSDのものかGNUのものかによって4行目のawk
の指定の仕方が異なります。
2019年に限定してベスト10を出してみる
単にgrep
を使うだけです。細かい期間指定をしたい場合はegrep
を使いましょう。
# Bash x BSDの場合 (※ $HISTTIMEFORMAT='%F %T')
history \
| grep 2019- \
| awk '{ for (i = 4; i < NF; i++) { printf("%s ", $i) } print $NF }' \
| sed 's/|/\'$'\n/g' \
| awk '{ print $1 }' \
| sort \
| uniq -c \
| sort -r \
| head
Zshで期間指定する場合はhisotry
に**-i**オプションを追加します。
3行目のawk
のiの開始は4に変わっています。
# Zsh x GNUの場合 (※ $HISTTIMEFORMAT='%F %T')
history -i 1 \
| grep 2019- \
| awk '{ for (i = 4; i < NF; i++) { printf("%s ", $i) } print $NF }' \
| sed 's/|/\n/g' \
| awk '{ print $1 }' \
| sort \
| uniq -c \
| sort -r \
| head
ちなみに私の自宅Macでの実行結果はこんな感じでした。
職場のMacでやってみるとまた結果が変わって面白いと思います。
201 vi
148 ll
111 git
96 cd
87 gc
79 kg
73 rm
72 k
66 brew
61 mv
ちなみに、aliasされたコマンドはvi='nvim'
、ll='ls -al'
、gc='git commit'
、kg='kubectl get'
、k='kubectl'
です。
解説
上記のコマンドはほとんど同じですが微妙に違うところがあります。
BashかZshかでhistory
の使い方や結果が異なりますし、BSDかGNUかでsed
の使い方が異なります。
history
まずはBashとZshで微妙に仕様が違うhistory
について見ていきましょう。
Bashでそのままコマンドを打ってみるとこのように表示されます。
$ history
1 2018-09-08 17:00 brew install ricty
2 2018-09-08 17:05 cp -f /usr/local/opt/ricty/share/fonts/Ricty*.ttf ~/Library/Fonts/
3 2018-09-08 18:29 brew search thunderbird
...
日付時刻付きで全件表示されます。
一方Zshで同様に打ってみると・・・
2593 ./init.sh
2594 ll ~/Library/Application\ Support/Code/User/
2595 gc -m "Add VSCode keybindings"
...
2610 git init
日付や時刻は表示されず最新16件のみ表示されます。
ただし、Bashのhistory
でも日付や時刻が表示されない場合があります。
それは環境変数HISTTIMEFORMATが設定されていない場合です。
この場合は(途中で設定したとしても)期間ごとに絞り込むことはできないので注意してください。
以降はHISTTIMEFORMATに%F %T
が設定されているという前提で話を進めます。
Bashはデフォルトで全件出ますし、HISTTIMEFORMATを指定していれば日付も出ますのでこれ以上詳しく話す必要もないでしょう。
Zshのhistory
の場合、全件出すには引数を指定する必要があります。
# 全件表示(1件目以降全て)
history 1
# 区間指定
history 10 20
また、先程説明したとおり日付時刻を表示に含めるには-iオプションを付け加えます。
Zshのhistory
のオプションについてより詳しく知りたくばman zshbuiltins
でhistory
の項目とfc -l
の項目を見てください。
そう、Zshのhistory
はfc -l
のaliasなのだそうです。
history
Same as fc -l.
historyの結果をコマンド種別で集計する
一旦間を飛ばして、シンプルにhistory
の結果をコマンドごとに集計するにはどうすればいいか考えてみます。
Bashのhistory
で説明します。
history
の1行ごとの出力は空白文字区切りで
インデックス 日付 時刻 コマンド サブコマンドやオプションなど
となっているので、パイプなどを考慮しなければawk '{ print $4 }'
に渡せばよいことがわかります。
そうしたら、uniq -c
で重複部分を消しつつカウントすればいいようですが、uniq
は連続した重複しか消すことができないので、uniq -c
する前に一旦sort
します。
# Bash
$ history | awk '{ print $4 }' | sort | uniq -c
...
1 c
3 cal
31 cat
116 cd
13 chmod
5 code
1 command
7 cp
60 curl
10 cut
...
あとはこれを降順にソートし直せば頻度順になります。
パイプした後のコマンドも集計に含めたい
パイプした後のコマンドも集計に含めたい場合どうすればいいでしょうか。
方法は色々あると思いますが、今回は|
を改行コードに置換するという方法を取りました。
ここでsed
がGNUかBSDのものかで置換の指定方法が若干異なります。
# BSD sed
sed 's/|/\'$'\n/g'
# GNU sed
sed 's/|/\n/g'
BSDのsed
で改行コードに変換する方法を調べていたら以下の記事が見つかりましたので、詳しく知りたければ読んでみてください
ただし、普通に改行するだけではコマンド部分とインデックスやサブコマンド以降と切り分けづらいです。
なので改行する前にコマンド部分以前をカット(コマンド部分以降を抽出)してしまいたいです。
それが
awk '{ for (i = 4; i < NF; i++) { printf("%s ", $i) } print $NF }'
の部分に当たります。
解説しておきながら実はawkの言語仕様については詳しくないので、forの中身がfor (i = n; i <= NF; i++)
でなかったり、通常最後列の文字列が入る$NFにfor pirntfした内容が入っている理由を知っている方がいたら教えていただけると助かります。
つまり、ここまでの流れをまとめると
# Bash x BSDの場合 (※ $HISTTIMEFORMAT='%F %T')
history \
| awk '{ for (i = 4; i < NF; i++) { printf("%s ", $i) } print $NF }' \
| sed 's/|/\'$'\n/g' \
| awk '{ print $1 }' \
| sort \
| uniq -c \
| sort -r \
| head
のコマンドはおおよそ
コマンド履歴を表示する \
| 余分な列をカットする \
| パイプ後もカウントするよう改行コードに置換する \
| 重複カウントしやすいようコマンド部分のみを抽出する \
| 重複カウントしやすいようソートして重複数をカウントする \
| 頻度順にソートする
という意味になります。
おまけ
history
の最大件数は環境変数HISTSIZEで決まっています。
コマンド履歴は今回のような使い方以外にもCtrl + Rでの検索時に重宝しますので、CUI操作をよくする人はより大きい数を設定しておくとより便利になります。
そして普段Macを使っている人でGNU sedを使いたい人はbrew install gnu-sed
してalias sed='gsed'
を.bashrcか.zshrcに書き加えておきましょう。
その他GNUコマンドのインストール方法に関しては、こちらの記事がとてもよくまとまっていました。