[bash]キャリッジリターンの使いどころ

2016年8月8日月曜日

bash シェルスクリプト

Unixの改行コードはLFでWindowsの改行コードはCRLFだ、なんていう話はよく出てくる。

CR(carriage return)というのは本来、現在位置を行の先頭に戻すだけで次の行には進まない。
LF(line feed)は逆に行中の位置は変えずに現在位置を次の行に進める。

じゃあ、CRのみを使うのはどういった場面があるだろうか。
例えば、コマンドラインで進捗状況を表示することを考えよう。

count.sh
#!/bin/sh

for i in $( seq 10 )
do
  # \rで行の先頭に戻ってから出力、改行はしない。
  printf "\r%2s/10" $i
  sleep 1
done
echo ''
$ ./count.sh
 1/10

...1秒ごとに更新される。

$ ./count.sh
10/10
$

うまく出力されているね。
でも、出力する文字列よりも、すでにスクリーンに出力されている文字列の方が長い場合はどうなってしまうだろうか。

rainspain1.sh
#!/bin/sh
printf "In Hartford, Hereford and Hampshire hurricanes hardly ever happen."
sleep 1
printf "\rThe rain in Spain stays mainly in the plain."
echo ''
$ ./rainspain1.sh
The rain in Spain stays mainly in the plain.es hardly ever happen.
$

おっと、前の出力文字が残ってしまった!
\rは位置を移動しているだけなので、文字を消しているわけではないので当然こうなるよね。
すでに出力されている文字を消すにはどうすればいいんだろう。

rainspain2.sh
#!/bin/sh
printf "In Hartford, Hereford and Hampshire hurricanes hardly ever happen."
sleep 1
printf "\r%$( tput cols )s"
sleep 1
printf "\rThe rain in Spain stays mainly in the plain."
echo ''
$ ./rainspain2.sh
The rain in Spain stays mainly in the plain.
$

今度は前の出力文字が消えてから、新しい文字列が出力されたね。

tputはターミナルの設定を取得したり、設定するコマンドで、tput colsは画面幅(文字数)を取得するよ。
こんな感じ。
$ tput cols
80
$

だから printf "\r%$( tput cols )s" で、行の先頭に戻ってから、画面幅分の空白文字を出力しているね。
空白文字で前回の文字を上書きしているってわけだ。