【Mac】ターミナルでカウントダウン
前に、カップメンの 3 分間をターミナルで測れたらいいのに、という話で、
sleep 180; say 'ラーメンが出来ました'
というのを紹介した(【Mac】長ったらしいコマンドを短く | 林檎コンピュータ)。
この 180 秒というのを、カウントダウンさせて表示したい、と思う。
シェルスクリプト
🍜 ramen.sh#!/bin/bash
MIN=$1
jikan=`echo $((MIN * 60))`
date
while [ 0 -lt $jikan ]
do
echo -en ' 🍜 '"$jikan\r"
sleep 1
jikan=`echo $((jikan - 1))`
done
date
say '時間です'
肝となるのは、
echo -en ' 🍜 '"$jikan\r"
の、 -e
オプションと \r
である。
エスケープ文字を出力する
-e
オプションは、バックスラッシュでエスケープされた、エスケープ文字というものを出力する。
\r
はカーソルを行頭に戻してくれるエスケープ文字だ。キャリッジリターンというらしい。 -e
をつけることで、キャリッジリターンを機能させることが出来る。
たとえば \n
は UTF-8 において改行を意味するエスケープ文字として有名だ。これを echo
するには -e
オプションをつける。
# \n が文字列として出力される。
MacBook:~ user$ echo "hello\nworld"⏎
hello\nworld
# \n が改行として出力される。
MacBook:~ user$ echo -e "hello\nworld"⏎
hello
world
ちなみに echo -n
は出力を改行させないオプションだ。
キャリッジリターンは、カーソルを行頭に戻して次の出力を待つ。
以下を試すと 1s……2s……3s と一秒ごとに表示が移りかわっていく。
echo -en " 1s\r"; sleep 1; echo -en " 2s\r"; sleep 1; echo ' 3s'
上掲した ramen.sh で注意すべきは、 #!/bin/sh
だと動かない(おもに Mac においてだと思われる)。
-en 59
-en 58
-en 57
……
などと表示されてしまう。 #!/bin/bash
だとちゃんと動く。
もしくは、 printf
を使うといいらしい。
$ printf "彼女が\n好き\n"⏎
彼女が
好き
$ printf "彼女が好き\r彼氏\n"⏎
彼氏が好き
エスケープ文字は、ほかにもいくつかある。
# \a
# アラームを鳴らす。
$ echo -e "\a"
# \b
# バックスペース
MacBook:~ user$ echo -e "abcdefg\b\b"
abcdeMacBook:~ user$
# \c
# 改行しない
MacBook:~ user$ echo -e "改行なし\c"
改行なしMacBook:~ user$
# \f
# 改ページ(フォームフィード)
MacBook:~ user$ echo -e "\f"
MacBook:~ user$
# \n
# 改行
$ echo -e "ここで\nはきものをぬいでください"
ここで
はきものをぬいでください
# \r
# キャリッジリターン
$ echo -e "明日やろう\r馬鹿"
馬鹿やろう
# \t
# タブ
MacBook:~ amenecobook$ echo -e "\t--遠い昔、遥か彼方の銀河系で"
--遠い昔、遥か彼方の銀河系で
# \v
# 縦のタブ
MacBook:~ user$ echo -e "\v"
MacBook:~ user$
# \0nnn
# 8 進数の ASCII 文字
$ echo -e "\0102\0101\0113\0101"
BAKA
# \xnn
# 16 進数の ASCII 文字
$ echo -e "\x41\x48\x4f"
AHO
# \\
# バックスラッシュ
$ echo -e "\\"
\
シェルスクリプトの修正
上記スクリプトファイルは、平気で数秒ずれる。ひどい出来だ。
また、ひと桁秒の表示がなんかおかしい。
以下のように書き直した。
#!/bin/bash
MIN=$1
jikan=`echo $((MIN * 60))`
Now=`date "+%s"`
target=`echo $((Now + jikan))`
date
while [ 0 -lt $jikan ]
do
fun=`echo $((jikan / 60))`
byo=`echo $((jikan % 60))`
echo -en ' 🍜 '"$fun 分 $byo 秒\r"
sleep 1
Now=`date "+%s"`
jikan=`echo $((target - Now))`
done
date
osascript -e 'display notification "時間です。"'
say '時間です'
これで多少マシになった、と思う。
時刻を入力してカウントダウン
ramen.sh は分を引数にしてカウントダウンしたが、そうではなく、時間を入力してアラームを設定したい場合もある。
夜、七時半に家を出なくてはならないとき、 alerm.sh 1930
とやって時刻を知らせてくれれば便利だろう。
以前、カウントダウンに関するスクリプトを書いたので、それを流用して作ってみる。
#!/bin/bash
function keisan() {
time=$1
fun=`echo $((time/60))`
byo=`echo $((time%60))`
if test $fun -ge 60; then
ji=`echo $((fun/60))`
fun2=`echo $((fun%60))`
if test $ji -ge 24; then
nichi=`echo $((ji/24))`
ji2=`echo $((ji%24))`
echo "${nichi}"'日'"${ji2}"'時間'
else
echo "${ji}"'時間'"${fun2}"'分'
fi
else
echo "${fun}"'分'"${byo}"'秒'
fi
}
if test -z "$1"; then
echo '時刻を、24時間表記の 4 桁の数字で入力してください'
exit 1
else
jikoku=$1
fi
if [[ "$jikoku" =~ ^[0-9]+$ ]]; then #引数が数字かどうか
keta=`echo ${#jikoku}` #桁数を調べる
if ! test "$keta" -eq 4; then
echo '時刻を、24時間表記の 4 桁の数字で入力してください'
exit 1
fi
else
echo '時刻を、24時間表記の 4 桁の数字で入力してください'
exit 1
fi
nowYMD=`date "+%Y%m%d"`
tgTime=`echo "$nowYMD""$jikoku"'00'`
target=`date -j -f "%Y%m%d%H%M%S" "$tgTime" "+%s"`
nowTime=`date "+%s"`
nokori=`echo $((target - nowTime))`
# 残り時間がマイナスなら、明日とみて 86400 秒足す。
if test $nokori -le 0; then
target=`echo $((target + 86400))`
nokori=`echo $((target - nowTime))`
fi
targetJST=`date -r "$target"`
echo "$targetJST" 'まで'
while [ 0 -lt $nokori ]
do
seconds=`keisan "$nokori"`
echo -en ' > '"$seconds\r"
sleep 1
nowTime=`date "+%s"`
nokori=`echo $((target - nowTime))`
done
date
say '時間です'
対応しているのは 24 時間以内の時間だ。
引数の入力は 0000 〜 2359 までの 24 時間表記。コロンなどは入れない。
say '時間です'
ではなく、
afplay 音声ファイル
のほうがいいかもしれない。けっこう聞き逃すので。
途中でキャンセルしたくなったら、control + c を押す。