ターミナルでCUIを操作してたりすると、たまーにスピナー(棒がくるくる回ってるやつ)が表示されるコマンドがあったりする。その他にも、zshのtab補完のようなプロンプトの下でアニメーションする処理もある。 こういった、ターミナル上で文字を使って動く処理は、果たしてどうやって作ってるのだろう?と思ったので、簡単に作り方をまとめてみることにした。

1. 「\r(復帰コード)」を使う

1行のアニメーションであれば、一番手軽な方法。 bashやzshでは、\rキャリッジリターン(復帰コード) という制御コードを指定できる。この制御コードがあった場合、カーソルを行頭に戻すという処理が行われる。

Windowsの改行コード(CR+LF)に含まれているので、WindowsとLinuxでファイルのやり取りをする際などに邪魔な存在として認識されることがおおいのだけど、これを利用することでスピナーのような、一行で完結するアニメーションが作成できる。

#!/bin/bash

sp="/-\|"
i=1
while :; do
    sleep 0.2
    char="${sp:i++%${#sp}:1}" # パラメータ展開を使って一文字づつ取得する
    printf "%s\r" ${char}
done

このようなコードを実行することで、コンソールで動作する単純なスピナーを作ることができる。 ただ、注意したいのは\rでは行の中で前に記述した箇所は消えないということ。 なので、前に出力した内容よりも短い内容を出力する場合は、スペースで埋めてしまうか後述するANSI Escapeを使うのがいいだろう。

blacknon@MAIN-DESKTOP:~$ echo -e "aaaaaaaaaaaaa\rbbbbbbbbb\rccc"
cccbbbbbbaaaa

また、複数行が動くアニメーションを実装するのも厳しいので、そういった場合でもANSI Escapeを使ったほうがいいだろう。

2. clearを使う

複数行のアニメーションを作る際、もし画面いっぱいに使っても構わないのであれば、ちょっと乱暴なやり方にはなるのだけど都度clearでコンソールをクリアすることでアニメーションを作る事ができる。

#!/bin/bash
clear

while :; do
    echo あいうえお
    echo かきくけこ
    sleep 1
    clear

    echo さしすせそに
    echo たちつてと
    sleep 1
    clear

    echo なに
    echo ぬねの
    echo はっひふっへほー
    sleep 1
    clear
done

ちょっと乱暴ではあるのだけど、かなり簡単に作れるので、単純な内容であればこれで作ってしまっても良いのではないだろうか。

3. ANSI Escapeを使う

プロンプトの下で複数行で動くなど、ちょっと凝ったアニメーションを作るのであれば、\rclearではなく、ANSI Escape を利用する方法がある。 ANSI Escapeというと、PS1とかで色付けしたりのがよく利用されると思うのだが、カーソルを移動させたり行の内容をclearさせるなど、できることがかなり多い。

以下、アニメーションを作成する際に使えるものだけを抜粋。

エスケープシーケンス 説明
\e[nA カーソルを上にn移動
\e[nB カーソルを下にn移動
\e[nC カーソルを右にn移動
\e[nD カーソルを左にn移動
\e[nE カーソルをn行下の先頭に移動
\e[nF カーソルをn行上の先頭に移動
\e[nG カーソルを左からnに移動
\e[n;mH カーソルを上からn、左からmに移動
\e[nJ 画面を消去する。
nに入る値はそれぞれ
- 0(カーソルより後ろを消去)
- 1(カーソルより前を消去)
- 2(画面全体を消去)
\e[nK 行を消去する。
nに入る値はそれぞれ
- 0(カーソルより後ろを消去)
- 1(カーソルより前を消去)
- 2(行全体を消去)

サンプルとしては、ちょっと前に書いたやる夫が煽ってくるcommnad_not_found_hundleあたりがいいだろう。 ANSI Escapeを使えば、以下のようなアニメーションを作ることができる。

ちゃんとしたアニメーションを作るのであればANSI Escapeが一番かゆいところに手が届く感じではないだろうか。 ただ、そこまでお手軽とはいえないかな?と思うので、他の方法との使い分けになるのかな。