30歳からのプログラミング

30歳無職から独学でプログラミングを開始した人間の記録。

ターミナルで直前に実行したコマンドをクリップボードにコピーする

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という文字列が格納されている。

参考資料

『アルゴリズム図鑑 絵で見てわかる26のアルゴリズム』を読んだ

プログラマとしての基礎体力をつける、が今の関心事なので、アルゴリズムとデータ構造についても勉強しようと思っている。
その第一歩として、評判のよかった本書を読んだ。評判通りの良書だった。

www.shoeisha.co.jp

Apple の「Best of 2016」の一つにも選ばれたアプリの、書籍版。

とにかく感動するほど分かりやすい。
書籍なのでアニメーションはないが、処理の流れを省略することなく、1ステップ毎に大きくてカラフルなイラストで説明しているから、アニメーションを見ているのと変わらない。説明も、とにかく丁寧で、それでいて無駄を削ぎ落としたシンプルなものになっている。

あくまでもアルゴリズムやデータ構造そのものの説明に徹しているため、実装やプログラミング言語は一切出てこない。
そのため、プログラミング未経験者でも読めると思う。本書を読むのに必要なのは、アルゴリズムとデータ構造に対する関心のみだと思う。
逆にプログラミング経験者は、自分の好きな言語で実装してみると勉強になるはず。ものによっては本書の内容だけでは実装に落とし込めないけれども。
自分も JavaScript でいくつか書いてみた。

github.com

セキュリティを成り立たせている技術のアルゴリズムにも触れていて、よかった。
今まで読んだ公開鍵暗号方式の説明のなかで、一番分かりやすかった。あくまでも仕組みの説明に留めており、現実の実装には触れていないからだと思う。
構成も秀逸で、まず共通鍵暗号方式について解説し、それが抱える鍵配送問題に触れる。そのうえで、鍵配送問題を克服できる公開鍵暗号方式について解説している。次に、公開鍵暗号方式の問題点を紹介し、その対策として TLS のようなハイブリッド暗号方式やデジタル署名を紹介する。
「現実的な説明」をしようとすると、これらの要素を一気に伝えることになり、理解するための難易度が上がってしまう。

知識がなくても読めると書いたが、計算量オーダーの部分だけは数式やlogが出てくる。が、本書の主題ではないし、問題ないと思う。自分も読み飛ばした。

本書で学ぶような内容を実務で使ったことはないし、必要になったこともない。
だがそれは、その程度の仕事しかしてこなかった、ということだ。そして、実力のない自分は、その程度の仕事しか出来ない。
人気のライブラリを使って機能要件を何となく満たすだけ。

以下の記事に出てくる「完全に仕様を満たさないものや過多な機能を提供するライブラリであっても、作るアプリの仕様をそのライブラリに合わせて変えるなど工夫して使うようにしていました。仕様がライブラリに依存してしまっていたのです」という状態であり、ただライブラリを使うだけのプログラマ。

kawasin73.hatenablog.com kawasin73.hatenablog.com

このブログの方は東大に通っていて来春からは Google で働くらしいので自分とは全然違うのだが、問題意識や危機感は似ている。
このままでは大成できないし、作る側のプログラマになりたい。自前主義というわけではなく、トレードオフを考慮して、必要に応じて自分で作ることも出来るプログラマでいたい。ライブラリが上手く動作しないときに、コードを読んで調べられるプログラマでいたい。
「このままでいいわけがない」という思いが募り、勉強を始めた。あまり時間がないからあれもこれもは出来ないが、とにかくやるしかない。