| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄| | 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
必要なも
今回のスクリプトでは、以下のソフトを利用する。
tmuxCUIで画面分割やタブ作成みたいなことができる子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 |