| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄| | TIME OUT | |__________| (\__/) || (•ㅅ•) || / づ
はじめに
「好奇心」というのは大切だと思う。
しかし同時に、厄介なものでもある。
例えば、コードを書こうとエディタを開く。
書いている途中、エディタの挙動がおかしいことに気付く。
よく分からないのでネットで調べてみる。
見ていたサイトで、関係のない面白そうな設定を見つける。
ワクワクしながら試してみる。
うーん、動かない。。。何が原因?
と、その設定について調べはじめる。
調べて試してを何度か繰り返し、ようやく動く。
めでたしめでたし。
さて、、、あれ、そもそも何してたんだっけ?
あーそうだそうだ、コードを書いていたんだった。
と、コードを書こうとエディタを開く。
書いている途中、エディタの挙動がおかしいことに (以下略
この厄介な「好奇心」と付き合っていくために、「作業を時間で区切る」という習慣を付けたいと思う。
先の例で言えば、今から調べものを10分だけやるぞ。
と、決めておけば、10分後に進捗と作業を見直す機会を持てる。
例え途中で横道にそれたとしても、10分後に当初の目的を思い出すことができそうだ。
よし、じゃあまずは、時間を計って経過時間後に通知するシェルスクリプトを作ろう。
sleep
コマンドを使えば、割と簡単に作れそうだ。
と、おもちゃを作る動機づけはこんなとこ。 やってみよう。
つくるもの
実行例
今回作るものは、こんな感じで使える。 ちょっとしたスクリプトを用意すれば繰り返し実行もできる。
(以降、tell
という名前でシェルスクリプトを作成した前提で説明)
> tell 10m # 10分後にデフォルトメッセージを表示 > tell 10m "THE END" # 10分後に指定したメッセージを表示 > tell 1h -c "tmux lock-server" # 1時間後に任意のコマンドを実行
指定時間経過後に、下記のようなうさぎちゃんが問答無用で全力で割り込んでくる。
なかなか鬱陶しくも、愛おしい。
| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄| | take a | | break! | |__________| (\__/) || (•ㅅ•) || / づ
メッセージは、新たに作成するtmux
のウィンドウに出力している。
うさぎちゃんは、bunnysay
コマンドによって出力している。
また、任意のコマンド指定時は、上記メッセージは表示せずに指定したコマンドを実行するだけ。
ヘルプメッセージ
tell wait_time[hms] [-c command | message] wait_time wait time[unit] [unit] h: hours = wait_seconds * 3600 [sec] m: minutes = wait_seconds * 60 [sec] s: seconds = wait_seconds [sec] (default) -c command execute command after wait_time default) tmux new window & popup message message any message
必要なも
今回のスクリプトでは、以下のソフトを利用する。
tmux
CUIで画面分割やタブ作成みたいなことができる子bunnysay
うさぎちゃんがメッセージを表示してくれる
FreeBSDなら、下記コマンドでインストールできる。(root権限で実行してね)
> pkg tmux bunnysay
上記ソフトがない場合、指定時間経過後にecho
でメッセージを出力するだけの寂しい動作になる。
それに、エディタなどを開いていると、悲惨な表示になる。
とはいえ、そこはシェルスクリプト。
あなたの好きなコマンドに置き換えることは簡単だろう。
コード
今回用意したコードはこちら。
長いけど、結局やっていることは最後のsleep
コマンドの1行。
あとは、そこに渡すパラメータ作成が大半。
何か変なところがあれば、ご指摘頂けると幸いです。
ざっくり処理ブロックを説明すると、以下となる。
2行目以降
ヘルプ作成20行目以降
引数をsleep
コマンドに渡す秒単位に変換35行目以降
不正な時間指定だった場合、エラーメッセージを出して終了43行目以降
指定時間経過後に、実行するコマンドを設定50行目以降
tmux
で新規ウィンドウを作成し、メッセージを出力する70行目以降
バックグラウンドでsleep
コマンドにより待機後、コマンドを実行
少し込み入っている以下について補足しておこう。
- 引数の時間変換
tmux
のウィンドウ作成
補足) 時間の変換
引数の時間を秒単位に変換する処理は、20行目以降の下記コード。
# set wait seconds if [ ${#1} -ge 2 ]; then target_time=`echo "$1" | cut -c 1-\`expr ${#1} - 1\`` fi case "$1" in *h ) wait_sec=`expr $target_time \* 3600` ;; *m ) wait_sec=`expr $target_time \* 60` ;; *s ) wait_sec="$target_time" ;; * ) wait_sec="$1" ;; esac # wait_sec is digit? if [ -z "`echo "$wait_sec" | grep '^[0-9]\{1,\}$'`" ]; then echo "Invalid wait time!: `$1`" usage_exit fi
最初のifブロックでは、cut
コマンドで引数の指定時間から末尾1文字を省いた数値部分を抽出している。
1文字の場合、cut
でエラーが出ないようにifで囲っている。
(cut -c 1-0
という不正な指定になってしまう)
もし、2文字以上の数値だけの場合は、この変数は実際には使わない。
次のcase
文の*
のパターンで、引数を待機秒数にセットする。
case文の*h
,*m
のパターンでは、それぞれ3600
か60
をかけて秒単位に変換している。
*s
のパターンでは、末尾からs
を除いた数値部分をそのまま待機秒数にセットしている。
最後のifで、「1文字以上の数字の繰り返し」以外だった場合は、エラーメッセージで終了する。
補足) tmuxのウィンドウ作成
tmux
のウィンドウ作成は、下記関数で行っている。
popup_window() { [ -n "$*" ] && msg="$*" || msg="take a break!" [ -n "`which bunnysay`" ] && print_cmd=bunnysay || print_cmd=echo # make popup window tmux new-window 2> /dev/null && ( WIN_ID=`tmux display -p '#I'` # print message tmux pipe-pane -I -t:$WIN_ID 'echo " "' tmux pipe-pane -I -t:$WIN_ID "echo '$print_cmd \"$msg\"'" # tmux clock-mode -t:$WIN_ID 2> /dev/null # auto close sleep 3 tmux kill-window -t:$WIN_ID ) 2> /dev/null \ || ( echo " "; $print_cmd "$msg" ) 1>&2 }
最初の処理の2行は、メッセージとメッセージを出力するコマンドの設定。
引数があれば引数のメッセージを、なければ既定のメッセージをセットする。
bunnysay
があればbunnysay
を、なければecho
を出力コマンドにセットしている。
tmux new-window
の行で、tmuxのウィンドウを新たに作成している。
作成に成功した場合は、&& (〜)
ブロックの中の処理を実行する。
失敗した場合は、最後の行の|| (〜)
ブロックの中の処理を実行する。
つまり、tmux
が動いてないときはecho
するだけになる。
&& (〜)
ブロックの中では、まず作成したウィンドウのIDを取得しておく。
以降のtmuxのコマンドでは、-t
オプションにで作成したウィンドウに対してコマンドを実行する。
次に、tmux pip-pane
コマンドで、作成したウィンドウにメッセージを出力している。
最初のecho " "
は、最初に改行を出力するためのもの。なかなかの手抜き感。
コメントアウトしているが、tmux clock-mode
により全画面で時間を表示をすることもできる。
ただし、何かキーを押したらすぐに消えてしまうため、実用性はあまりないかもしれない。
最後に、sleep
コマンドで待機後、作成したウィンドウを自動で閉じる。
待機時間は好きに変えていいし、不要なら削除するといい。
オプションで選択できるようにしてもいいかもしれない。
なお、ブロック中の標準エラー出力は/dev/null
に捨てている。
連続実行による途中経過時間の表示と、繰り返しと、中断と
下記のような別のシェルスクリプトを用意しておけば、途中経過時間も表示できたりする。
さらに、ひたすら繰り返せる。
経過表示分のプロセスを追加で立ち上げることになるけれども。
#!/bin/sh tell 10m "10 min" tell 20m "20 min" tell 25m tell 30m "Get to work!" tell 30m -c "$0"
でも止める場合kill
が大変なので、以下のようなスクリプトも用意した方が良いかも。
(fzf
利用しているので、インストールして試してね=>こちらの記事など参考)
#!/bin/sh selected=`ps ax \ | grep tell \ | grep -v "grep tell" \ | grep -v "$0" \ | fzf -m` [ -z "$selected" ] && exit 0 echo "$selected" \ | sed 's/^[ ]*\([0-9]\{1,\}\)[^ ]*.*$/\1/' \ | xargs kill
ps
コマンドの出力がFreeBSD以外でどうなるか自信がないので、Linuxなどの人は必要ならsed
のパターンを置換えてほしい。
それにしても、一括でkillする方法ないのかな。
fzf
を使っているので、だいぶ楽ではあるのだけれども。
何か素敵殺法をご存知の方は、コメント頂けると幸いです。
実際に使ってみると
今回のスクリプトは、デフォルトではどんな作業をしていようと、急にウィンドウを切り替えて、全力でうさきちゃんが邪魔をしてくる。
作業タイマーとしては邪魔かもしれない。
実用としては音を鳴らすなど、お好みの通知方法に変えてほしい。
また、通知の画面切り替えの際にtmux
のウィンドウ操作をごちゃごちゃやると、きっとバグる。
ウィンドウIDを指定して多少はバグりにくくしているものの、完全ではないと思うので悪しからず。
最後に
もし、今回のスクリプトに「何時」という時間指定で予約する機能を追加したい場合、どうすればいいだろうか。
シェルスクリプトで時間の計算は面倒臭そうだ。
何かコマンドに頼るのものいいかもしれないが、date
をsleep
しながらポーリングする方法なら楽に実装できる気がする。
興味がある人は、チャレンジしてみてはいかがだろうか。
興味。
それは「好奇心」がある限り尽きることはない。
ただ、残念ながら人生で使える時間は有限だ。
一生かかっても知らないことや理解できないことの方が多い。
時間というものさしを使いながら、目の前のお仕事に集中しましょ。
確認環境
PC | Thinkpad X1 Carbon 2nd Gen |
OS | 12.0-RELEASE-p7 |
tmux | tmux 2.9a |
bunnysay | 1.1_2 |