はじめに
この記事ではUNIXサーバ管理という側面からのシェルスクリプトプログラミングを取り上げます。またシェルスクリプトを自作するときのポイントも解説します。
UNIXサーバを管理するときは、道具としてのシェルスクリプトが重宝します。常時ログを監視するにしても、入力するコマンドをまとめるにしても、UNIXに用意されているコマンドを有効活用するにはシェルスクリプトが効果的です。
シェルスクリプトは、コマンドを入力する作業の延長線でプログラミングができるという容易な側面を持っていますが、正しく理解しようとすると多くのルールやコマンドを覚えなければならないというやっかいな側面も持っています。
シェルスクリプトを習得する方法はいくつかあります。じっくり時間がとれるならどの学習方法でもよいのですが、入社後にUNIXサーバを管理することになり、緊急に使えるようにならなければならないという逼迫した状況であれば、他人のシェルスクリプトを読んで用途に応じて編集していくという方法が効果的です。
シェルスクリプトは現在主流になっているプログラミング言語とちがって、空白改行の扱いがコマンドプロンプト的であるなど、それまでのプログラミングの経験からでは理解しずらい面があります。よって、言語仕様を把握して一から覚えていくのも手ですが、自分が目的としているシェルスクリプトを探してきて編集して使う方が効果的に覚えられますし、実用的でもあります。これからじっくりシェルスクリプトを学ぼうとしている方にも、この学習方法はお薦めできます。
対象読者
本稿では、ある程度シェルスクリプトを使うことができる、中級者から上級者を読者として想定します。
必要な環境
紹介するシェルスクリプトを実行するために必要となる環境は、最近のUNIX互換OSです。こちらではFreeBSD 5.3、NetBSD 2.0、OpenBSD 3.6、Novel SuSE 9.1、Solaris8、Solaris10で動作を確認してあります。
シェルスクリプト
シェルスクリプトでどういったことが実現できるのでしょうか。もちろん、やろうと思えばなんでもできますが、シェルスクリプトにはそれに適した用途範囲があります。サーバの起動停止処理であったり、既存のコマンドを組み合わせる場合に効果を発揮します。
以降では、シェルスクリプトで実現できることを示すという目的で、topless
というシェルスクリプトを取り上げます。topless
はUNIXサーバ管理業務で使用できる実用的なスクリプトです。シェルスクリプトとしては難しい部類に入り、シェルスクリプトの限界を把握しておく意味で効果的な教材といえます。
topless
シェルスクリプトは、2005年春におこなわれた関東甲信越BUG合同合宿において開発されました。
合宿の途中、ある開発者の方がプロンプト2.1のようなコマンドを教えてくださいました。^[
と表示されている文字列はESCコード(1BH)です。
# clear; while :; do echo -n "^[[1;1H"; netstat -nr; sleep 1; done
このコマンドを実行すると、netstat -nr
というネットワークの接続状況を表示するコマンドが、1秒おきに端末に再描画されます。プロセスの状況を画面に表示するtop
というコマンドがありますが、その出力内容がnetstat -nr
の出力結果になったような感じです。
この処理の要は、clear
で端末をクリアしたあとで、echo "^[[1;1H"
として画面左上隅にカーソルを移動させるエスケープシーケンスを端末に送って、コマンドの出力がつねに同じ場所に行われるようにしている点にあります。
その場でこの1行コマンドが便利だということになり、いくつか機能を追加し常用できるシェルスクリプトまでおこすことになりました。それがtopless
です。topless
という名前は、top
のような出力をおこない、less
のようにコマンドのページャとして扱うというところから命名されています。FreeBSDで開発されましたが、のちに使用者からのフィードバックをもとにNetBSD, OpenBSD, Linux, Solarisでも動作するように変更されました。
topless
UNIX環境がある方は、topless
をダウンロードして、プロンプト3.1のようにコマンドを実行してみてください。netstat -nr
の出力結果が、1秒おきに更新される様子がわかります。
# topless netstat -nr
次に、-c
を指定してプロンプト3.2のように実行してみてください。出力結果において、前回の出力と違うところが赤色で表示されるようになります。この前回と出力が違っている行は色をつけるという機能が、topless
で実現されているもっとも注目される機能です。
# topless -c netstat -nr
さらに-n 5
という指定を行うと、過去5回分まで記録し、変更があった行に色が残るようになります。ネットワークを監視する場合など、しばらくその結果が残っている方が好ましいことが多いため、この機能がつけられました。シェルスクリプトで実現するのは、この機能あたりまでが限界でしょう。これ以上は複雑すぎて、シェルスクリプトで実現するにはあまり利益がなくなってきます。
# topless -c -n 5 netstat -nr
ほかにも、-h
でヘルプの表示、-v
でバージョンの表示、-s
で更新時間の指定をおこなうことができ、コマンドを終了するためのキーが[Ctrl]+[c]だけではなく、top
のように[q]キーでも行えるようにしてあります。シェルスクリプトとしてはかなり技巧が施されている方でしょう。このあたりまでくると、通常はC言語やほかのスクリプト言語で書き直したりするものです。シェルスクリプトで実現する機能としては、だいぶ難しいものとして参考にしてください。
本体の処理
まず処理の本体を探します。わからないコマンドが使われていたとしても、適当にあたりをつけて読み飛ばします。シェルスクリプトを読む場合、枝葉末節にはこだわらずどこが処理の本体かを探します。そうしないと、処理の枝葉末節が気になって全体が把握できなくなってしまうからです。
topless
は、前回の出力との違う部分に色をつける処理を実現するために、だいぶ込み入ったものになっていますが、オプションが指定されていない場合に実行されない部分を追っていて不要な部分を落し整理していくと、結局つぎのようなコードが処理の本体ということがわかります。
while : do buffer=$(eval ${@+"$@"}) echo -n "$es_cl$es_ho$buffer" sleep $waitsec done
これを突き止めることができれば、結局処理としてはプロンプト2.1と同じことが実行されていることがわかります。本体がわかれば、それをコアにして自分でスクリプトを拡張していくこともできます。
以降では処理の基本的なアイディアと流れを紹介します。topless
で使われているコマンドの意味やその使い方については、マニュアルをご覧ください。
表示の制御
topless
では、エスケープシーケンスを使って、文字列を色をつけたり、端末をクリアしたり、カーソルの位置を移動させたりしています。端末によってはエスケープシーケンスでかなり多くの操作を行うことができます。端末を駆使したい場合は一度エスケープシーケンスを調べておきましょう。代表的なエスケープシーケンスを表4.2に示します。
シーケンス | 内容 |
ESC[x;yH | 座標x,yにカーソルを移動 |
ESC[2J | クリア |
ESC[0J | その行をカーソルから行末までクリア |
ESC[0m | 通常の文字 |
ESC[1m | 太字文字 |
ESC[4m | 下線つき文字 |
ESC[5m | 点滅文字 |
ESC[7m | 反転文字 |
ESC[30m | 前景色:黒 |
ESC[31m | 前景色:赤 |
ESC[32m | 前景色:緑 |
ESC[33m | 前景色:黄 |
ESC[34m | 前景色:青 |
ESC[35m | 前景色:紫 |
ESC[36m | 前景色:水色 |
ESC[37m | 前景色:白 |
ESC[40m | 背景色:黒 |
ESC[41m | 背景色:赤 |
ESC[42m | 背景色:緑 |
ESC[43m | 背景色:黄 |
ESC[44m | 背景色:青 |
ESC[45m | 背景色:紫 |
ESC[46m | 背景色:水色 |
ESC[47m | 背景色:白 |
エスケープシーケンス自身の入力は、エディタごとに異なります。Emacsであれば[Ctrl]+[q]→[ESC]で、端末でエディタを使っている場合は[Ctrl]+[v]→[ESC]で、エスケープシーケンス自身を入力することができます。エスケープシーケンス自身を入力する方法がわからなかったり、シェルスクリプト内に直接エスケープシーケンスを書き込むことを避けたい場合は、リスト4.3のようにecho
やprintf
コマンドを使います。代表的なエスケープシーケンスはtput
コマンドでも出力させることができますので、それを使ってもいいでしょう。
color_red=$(echo -e "\e[31m") color_blu=$(echo -e "\e[34m") color_org=$(echo -e "\e[0m") es_cl=$(tput clear) # clear screen and home cursor es_ho=$(tput home) # home cursor
前回の出力と違うところに色をつける処理は、diff
コマンドを使うことでおこなっています。前回の出力と、今回の出力をファイルに保存して、diff
コマンドで違いを調べます。違いがある行とそうでない行は変数に状態を記録しておき、その変数をもとに色付けを行うエスケープシーケンスを付与したりしなかったりを選択しています。/bin/shのシェルスクリプトは配列を使うことができませんので、すべて変数として設定します。
複数回以前までの状態を記録する方法は、先ほどの変数に数字を設定することでおこなっています。変数にたとえば「5」という値を設定し、一回処理が終わるごとに数値を減算していきます。変数が「0」より大きい場合は色付けを実行し、そうでない場合は色付けを行わないということです。こうすれば、数回分を記録しておくことができます。
あと注目するべきは、空白タブがそのまま解釈されるようにwhile
構文で使用されているIFS
変数の指定です。while read
の処理では、空白タブが短縮されてしまうため、そのまま文字列として処理するためにIFS=
を指定しておきます。
echo "$buffer" | while IFS= read line do ....
あとはコマンドとして使用するために引数の処理を加え、割り込み時の処理を設定し、細かく調整を加えて仕上げています。
シェルスクリプトを編集する
シェルスクリプトの内容が把握できたら、練習する意味も込めて編集を行ってみましょう。たとえば、オプションのデフォルト設定を変更するといった簡単なところからはじめます。
topless
であれば、表示される色が赤では見にくいから青に変更するとか、標準の更新時間をもっと長くしたいとか、そういったところから始めます。そこから徐々に変更する場所を増やしていって、変更した場合の動作の違いを逐次調べながら作業をおこないます。実際に実行しながら編集すると、思いの外速くシェルスクリプトを理解することができるようになります。
編集し変更することになれてきたら、リスト4.1のように本体だけから自分でコマンドを組み立てて作成してみます。最初は元のシェルスクリプトを参考にしながら理解できる範囲内で作業をおこないます。わからない部分が出てきたら逐次マニュアルを調べながら作業を行います。
シェルスクリプトになれてきたら、最初から自分で作成してみます。いくつかのシェルスクリプトでこれを行えば、シェルスクリプトがだいぶ実用的に使えるようになるでしょう。
互換性について
UNIXサーバの管理をおこなうときは、一種類のOSで済むとは限りません。対象とするOSが複数ある場合は、作成したシェルスクリプトも複数のOSで動いてほしいことになります。
まず、シェルスクリプトには基本的に/bin/shを選択するようにします。/bin/shはほとんどのUNIXに用意されています。いったん作成したら、各種OSで動作を調べ、使用するシェルスクリプトの機能やコマンドをどのOSにもあるものに置き換えていきます。
たとえば、リスト6.1の処理はOS間の互換性をとるための処理です。OSごとの状況に応じて、実行するコマンドが違っていたり、自前で同じ名前のコマンドを用意しています。
tput_cll=$(tput ce 2> /dev/null) tput_cls=$(tput cd 2> /dev/null) es_ce=${tput_cll:-$(tput el)} es_cd=${tput_cls:-$(tput ed)} stty size > /dev/null 2>&1 && readonly sttysize='stty size' || readonly sttysize='/usr/ucb/stty size' type mktemp > /dev/null 2>&1 || mktemp() { > ${@+"$@".$$} chmod 600 ${@+"$@".$$} echo ${@+"$@".$$} }
/bin/shの機能はどのUNIXでも似ていますが、Solarisなど特定のOSはポリシーの違いから、ほかのOSで持っている機能が入っていないことがあります。そのような場合、Solaris版shに実装を合わせるか、Solarisでは/bin/shの代わりにbashを使ったりします。Solarisではbashを使うようにするといた処理を実現するには、たとえばリスト6.2のように記述します。
#!/bin/sh [ -z ${BOOTED-""} -a SunOS = `uname -s` ] && exec env BOOTED=yes bash "$0" "$@"
どこまで互換性を考慮するのかは、ケースバイケースで選択します。意味もなく互換性を考慮しても、使いにくくなるだけです。自分の使用する範囲内で適度におこなえば十分です。
まとめ
実用シェルスクリプトとしてtopless
を取り上げました。
シェルスクリプトはUNIXを活用するための効果的なツールです。適切な用途範囲であれば、短時間で効果的なコマンドを作成することができます。
シェルスクリプトは便利である反面、雑多でもあります。ひとつひとつを詳しくみていくと、枝葉末節にとらわれて作成できなくなったり、理解できなくなります。まず確実に動作するものを編集したり、確実に動作するところから広げていきます。
シェルスクリプトは便利な機能です。既存のものを利用するなどして、積極的に活用していきましょう。