memo

2018-12-26

terminal でカーソルを動かす方法

よくある手法として \r でカーソルを行頭に戻すというのがあって、例えば以下のような感じにして進捗表示に棒を回したりするけど:

$ spinner=('-' '\' '|' '/')
$ for i in {1..100}; do
> echo -n "${spinner[$(($i % 4))]}"
> echo -ne '\r'
> sleep 0.1
> done

しかし、じゃあコレを縦に3つ並べて回したい、となった時に \r だとどうしょうもないので、カーソルをもっと自由に動かす方法。

端末ごとに適切なエスケープシーケンスを吐いてやればカーソルを動かせる。 何が適切かは terminfo データベースに書いてある。

シェルからは tput コマンドを使って名前から対応するものを出力できる。 ということで、縦に3つ並べるのはこうなる:

$ spinner=('-' '\' '|' '/')
$ for i in {1..100}; do
> echo "${spinner[$(($i % 4))]}"
> echo "${spinner[$(($i % 4))]}"
> echo "${spinner[$(($i % 4))]}"
> tput cuu 3
> sleep 0.1
> done

(tput cuu 3 でカーソルを3つ上に動かす。その他、何が出来るかは man terminfo を後で読む。)

python だと curses モジュールで同様のことが出来る:

import curses
import sys
import time


SPINNER = list(r'-\|/')

curses.setupterm()

for i in range(100):
    print(SPINNER[i % 4])
    print(SPINNER[i % 4])
    print(SPINNER[i % 4])

    sys.stdout.buffer.write(curses.tparm(curses.tigetstr('cuu'), 3))
    sys.stdout.buffer.flush()
    time.sleep(0.1)