bash のバージョンは5.0.11(1)-release (x86_64-apple-darwin18.6.0)
。
動機
Git のコミットメッセージに、その差分を生み出したコマンドを書くことがよくある。
例えば以下のように。
$ yarn run lint:fix $ git commit -am '$ yarn run lint:fix'
コミットメッセージに何を書くべきかについてはいろんな意見があるが、何らかのコマンドを実行した結果をコミットする場合は、そのコマンドを書いておくのは悪くないと思う。何をした結果としてその差分が生まれたのかが明確で、デバッグや調査のときに役に立つ。
そして、その時に直近のコマンドをコピペするわけだが、これが面倒くさい。よく行う作業だけに、もっと効率化したい。
ワンライナーで書ける
ただ単にタイトルの内容を実現するだけなら、ワンライナーで書ける。
$ history -a && tail -n 2 ~/.bash_history | head -n 1 | tr -d '\n' | pbcopy
これで出来る。
history -a
実行したコマンドの履歴は~/.bash_history
というファイルに書かれている。
だが随時更新されているわけではない。そのため、何も考えずに~/.bash_history
の内容を取りに行くと、意図とは違う結果になる可能性がある。
任意のタイミングで~/.bash_history
を更新するにはhistory -a
というコマンドを実行すればよい。
tail -n 2 ~/.bash_history | head -n 1
~/.bash_history
への書き込みが終わったら、tail -n 2 ~/.bash_history
を実行して~/.bash_history
の末尾2行を取得する。
なぜ2行なのかというと、最後の1行は今実行したコマンド、つまりhistory -a && tail -n 2 ~/.bash_history | head -n 1 | tr -d '\n' | pbcopy
になっているから。そのため、目的のコマンドはその1つ前のものということになる。
そして、取得した2行の1行目をhead -n 1
で取得することで、目的のコマンドを取得できる。
| tr -d '\n'
これで目的のコマンドの文字列を取得できたが、この文字列の末尾には改行がついている。このままだとターミナル上でペーストするのに不都合が生じる。
tr
は文字列操作のためのコマンドで、対象の文字列 | tr -d '削除する文字列'
とすると、対象の文字列
から削除する文字列
を除去した文字列を出力する。
これを使って改行文字(\n
)を取り除く。
| pbcopy
最後にpbcopy
で、クリップボードにコピーする。
試しに以下の操作をすると、クリップボードにecho 1
がコピーされているのを確認できる。
$ echo 1 1 $ history -a && tail -n 2 ~/.bash_history | head -n 1 | tr -d '\n' | pbcopy
エイリアスへの設定
いちいちhistory -a && tail -n 2 ~/.bash_history | head -n 1 | tr -d '\n' | pbcopy
を実行するのはそれこそ手間なので、エイリアスに登録する。
エイリアスに登録するためにはまず、~/.bashrc
に登録する。このファイルがなければ作る。
以下の内容を書き込んで保存する。clc
というのは CopyLastCommand の略なので、これは好きな名前を使えばよい。
alias clc=`history -a && tail -n 2 ~/.bash_history | head -n 1 | tr -d '\n' | pbcopy`
次に、~/.bash_profile
が~/.bashrc
を読み込むようにする。source ~/.bashrc
という内容を書き込めばいいので、以下を実行する。
$ echo 'source ~/.bashrc' >> ~/.bash_profile
これで、次に bash にログインしたときからclc
を使えるようになる。すぐに使いたい場合は$ source ~/.bash_profile
を実行する。
だがここで問題が発生した。エイリアスに登録すると、なぜか改行の除去が上手くいかない。このままだと不便だが、シェルスクリプトにすると意図通りに動いたので、やむを得ずシェルスクリプトにすることにした。
シェルスクリプト化
copy_last_bash_history
という名前で、以下の内容のファイルを作る。
#!/bin/bash tail -n 2 ~/.bash_history | head -n 1 | tr -d '\n' | pbcopy
ファイルに実行権限を付与する。
$ chmod +x ファイルのパス
既にシェルスクリプト用のディレクトリがある場合はそこに置く。
まだない場合は、ディレクトリの作成と、パスを通す作業が必要になる。
ディレクトリの作成
今回は~/bin/
というディレクトリを作り、そこに自作のシェルスクリプトを置くようにした。
パスを通す
~/bin/
をコマンド検索パスに含めるための作業。
以下の内容を実行する。
$ echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bash_profile $ source ~/.bash_profile
これで、copy_last_bash_history
というコマンドを実行できるようになった。
エイリアスの設定を、以下に書き換える。
alias clc='history -a && copy_last_bash_history'
これで、$ clc
を実行すると直前のコマンドがクリップボードにコピーされるようになった。
改良
せっかくシェルスクリプトにしたので、もう少し改良してみた。
#!/bin/bash argument=$(($1)) if [ $argument = 0 ]; then argument=1 fi tail -n $(($argument+1)) ~/.bash_history | head -n 1 | tr -d '\n' | pbcopy pbpaste echo ''
まず、直前のコマンド以外もコピーできるようにした。対象を引数で指定する。
clc
あるいはclc 1
で直前のコマンドをコピー、clc 2
で2つ前のコマンドをコピー、clc 3
で3つ前、という具合。
引数を受け取れるように、エイリアスの末尾に$1
を追加する。
alias clc='history -a && copy_last_bash_history $1'
~/.bashrc
を更新したときは$ source ~/.bash_profile
か再ログインをしないと変更が反映されないので、忘れずに。
また、どのコマンドをコピーしたか分かるように、コピーしたコマンドをpbpaste
を使って出力するようにした。最後にecho ''
で改行を出力する。
例えば以下のように動作する。
$ echo foo foo $ echo bar bar $ clc 2 echo foo
このとき、クリップボードにはecho foo
という文字列が格納されている。