🐌

「あと5分で区切りますよー」の5分をカウントするシェルスクリプト

2021/12/13に公開

KPTのような全員で何かを書いて共有するタイプのミィーティングには「各々が書く」時間が存在する。[1]
その書く時間を計る時に、時計なりスマホのタイマー機能なり何なりで良いが、私がファシリテーションをしている時はシェルスクリプトで行うことがある。そういう趣味なので。

いくつかパターンがあるので紹介する。

環境

  • MacBook, Monterey (だけどよほど古い OS でなければ関係ないと思う)
  • zsh (だけど zsh の機能は使ってないと思う)

sleep + say

sleep 300 ; say -v Kyoko 'おわりだよ'

sleep コマンドは指定した秒だけ待ってくれるので 300秒経ったら次のコマンドが実行される。
say コマンドは文章を読み上げてくれる便利コマンドで、 -v Kyoko とすることで日本語も読み上げてくれる。 -v Kyoko にしないと英語で読み上げる。なので say 'time is up' でもよい。

for + clear + echo

for i in {300..0}; do clear; echo $i; sleep 1; done

300 から 0 まで 1秒ずつカウントダウンして表示する。
clear はターミナルの表示をお掃除するコマンドで、 echo するたびに画面をまっさらにしてカウントダウンしてるっぽくする。
for が終わった後に say コマンドで何か言わせてもよい。

nabeatsu

for i in {1..300}; do { echo $i | grep -o . ; factor $i; } | grep -q '\b3\b' && echo $i | sed 's/\(.\)..$/\1百 &/; s/.\{0,1\}\([^0]\)\(.\)$/\1十 &/; s/\(.*\) .*\(.\)$/\1 \2/; s/\([百十]\) 0/\1/; s/1\([百十]\)/\1/g; s/百/ヒャクwww/; s/十/ジュウwww/; s/1/イチwww/g; s/2/ニwww/g; s/3/サンwww/g; s/4$/シwww/; s/4/ヨンwww/g; s/5/ゴwww/g; s/6/ロクwww/g; s/7$/シチwww/; s/7/ナナwww/g; s/8/ハチwww/g; s/9/キュウwww/g' || echo $i; sleep 1; done

ただ数字がカウントダウンしているだけでは退屈な時はナベアツをする[2]

解説

3の倍数は factor コマンドで素因数分解した結果で判断する。

$ factor 123
123: 3 41
$ factor 123 | grep '\b3\b'
123: 3 41
$ factor 26 | grep '3' # '3' だと3がつく3以外の数も含むので \b が必要
26: 2 13

3がつくかどうかは grep で判断する。
factor の結果とまとめて判断できるよう grep -o . をしてから grep \b3\b をする。

$ echo 123 | grep -o .
1
2
3
$ echo 123 | grep -o . | grep '\b3\b'
3
$ echo 123 | grep '\b3\b' # 3の前後に文字があるので検出されない

# factor の結果とまとめて判断
$ { echo 123 | grep -o . ; factor 123 ; }
1
2
3
123: 3 41
$ { echo 123 | grep -o . ; factor 123 ; } | grep -q '\b3\b'
$ echo $?
0

アホになる出力をするために数値を日本語に変換する。
111 を日本語で発音すると ヒャク ジュウ イチ となる。

百の位は3桁目に文字があるかどうかで判別できる。見つかったら百と書き込んでおく。

$ echo 123 | sed 's/\(.\)..$/\1百/'
1

十の位も同じように2桁目に文字があるかどうかを見る。ただし 0 の時は日本語だと発音しないので無視する必要がある。[3]

$ echo 123 | sed 's/.\{0,1\}\([^0]\)\(.\)$/\1十/'
2

百の位と十の位をまとめて抽出する。元の文字列をそのまま出力する & を活用する。

$ echo 123 | sed 's/\(.\)..$/\1百 &/'
1123
$ echo 123 | sed 's/\(.\)..$/\1百 &/; s/.\{0,1\}\([^0]\)\(.\)$/\1十/'
12# 十の位が 0 の時は無視する
$ echo 103 | sed 's/\(.\)..$/\1百 &/; s/.\{0,1\}\([^0]\)\(.\)$/\1十/'
1103

最後に一の位も抽出する。
一の位も十の位と同様に 0 の時は発音しないことに注意する。[4]

# & を使って一の位を抽出するための数値を用意
$ echo 123 | sed 's/\(.\)..$/\1百 &/; s/.\{0,1\}\([^0]\)\(.\)$/\1十 &/'
12123

# 最後の一文字を抽出しつつ百の位や十の位の文字列も出力
$ echo 123 | sed 's/\(.\)..$/\1百 &/; s/.\{0,1\}\([^0]\)\(.\)$/\1十 &/;
s/\(.*\) .*\(.\)$/\1 \2/'
123

# 二桁以上の時で一の位が 0 の時は無視する処理を追加
$ echo 123 | sed 's/\(.\)..$/\1百 &/; s/.\{0,1\}\([^0]\)\(.\)$/\1十 &/;
s/\(.*\) .*\(.\)$/\1 \2/; s/\([百十]\) 0/\1/'
123

# 一の位が 0 の時は無視される
$ echo 120 | sed 's/\(.\)..$/\1百 &/; s/.\{0,1\}\([^0]\)\(.\)$/\1十 &/;
s/\(.*\) .*\(.\)$/\1 \2/; s/\([百十]\) 0/\1/'
12

百や十の位が1の時は数値は発音しないので取り除く。

$ echo 123 | sed 's/\(.\)..$/\1百 &/; s/.\{0,1\}\([^0]\)\(.\)$/\1十 &/;
s/\(.*\) .*\(.\)$/\1 \2/; s/\([百十]\) 0/\1/; s/1\([百十]\)/\1/g'23

日本語で発音する文字列に変換できたので、百は「ヒャクwww」といった具合にアホな発音に変換すればよい。

$ echo 123 | sed 's/\(.\)..$/\1百 &/; s/.\{0,1\}\([^0]\)\(.\)$/\1十 &/;
s/\(.*\) .*\(.\)$/\1 \2/; s/\([百十]\) 0/\1/; s/1\([百十]\)/\1/g;
s/百/ヒャクwww/; s/十/ジュウwww/; s/1/イチwww/g; s/2/ニwww/g; s/3/サンwww/g;
s/4$/シwww/; s/4/ヨンwww/g; s/5/ゴwww/g; s/6/ロクwww/g; s/7$/シチwww/;
s/7/ナナwww/g; s/8/ハチwww/g; s/9/キュウwww/g'
ヒャクwww ニwwwジュウwww サンwww

あとは3の倍数か3がつく数値かを判定する部分と組み合わせて for でループさせれば完成。
{ echo $i | grep -o . ; factor $i; } | grep -q '\b3\b' が偽だった時はもとの数値をそのまま表示させるために || echo $i を追加している。[5]

for i in {1..300}
do
  { echo $i | grep -o . ; factor $i; } | grep -q '\b3\b' && \
  echo $i | sed 's/\(.\)..$/\1百 &/; s/.\{0,1\}\([^0]\)\(.\)$/\1十 &/;
  s/\(.*\) .*\(.\)$/\1 \2/; s/\([百十]\) 0/\1/; s/1\([百十]\)/\1/g;
  s/百/ヒャクwww/; s/十/ジュウwww/; s/1/イチwww/g; s/2/ニwww/g; s/3/サンwww/g;
  s/4$/シwww/; s/4/ヨンwww/g; s/5/ゴwww/g; s/6/ロクwww/g; s/7$/シチwww/;
  s/7/ナナwww/g; s/8/ハチwww/g; s/9/キュウwww/g' || echo $i
  sleep 1
done

余談1

アホっぽい表現は 数え続ける世界のナベアツbot の方が好みです。バリエーション豊かです。

https://twitter.com/3_aho_bot/status/1470065629684445184

余談2

Ruby の humanize gem を使うともっと簡単に変換できます。

$ echo 123 | ruby -r'humanize' -ne 'puts $_.to_i.humanize(locale: :jp)'
百二十三
$ echo 123 | ruby -r'humanize' -ne 'puts $_.to_i.humanize(locale: :jp)' | sed 's/百/ヒャクwww/; s/十/ジュウwww/; s/一/イチwww/g; s/二/ニwww/g; s/三/サンwww/g; s/四$/シwww/; s/四/ヨンwww/g; s/五/ゴwww/g; s/六/ロクwww/g; s/七$/シチwww/; s/七/ナナwww/g; s/八/ハチwww/g; s/九/キュウwww/g'
ヒャクwwwニwwwジュウwwwサンwww

余談サンwww

nabeatsu コマンド を使えば一発です。[6]

$ nabeatsu 123
ヒャクニジュウサァンwww

余談4

この記事のタイトルは「ナベアツをシェルスクリプトで実装&解説する」が正しいかもしれない。

脚注
  1. 人数によっては前もって書いておくルールにしているところもあるだろうけど ↩︎

  2. 3の倍数と3がつく数字の時だけアホになること ↩︎

  3. 百の位が 0 の時も同じケアが必要だけど今回は5分(300秒)を計ることが目的なので無視している ↩︎

  4. 一桁の時の 0 は「ゼロ」と発音するけどアホになる数値ではないので無視している ↩︎

  5. ちなみに something && hoge || fugahoge の終了コードが偽になった時も fuga が実行されることには気をつけよう ↩︎

  6. 9 を キュ- ではなく キュウ と表現しているので Twitter Bot とは別ものっぽいです ↩︎

Discussion