連載
» 2012年02月28日 00時00分 UPDATE

スマートな紳士のためのシェルスクリプト(4):一見読みにくい記法もシェルスクリプトの流儀 (1/2)

前回に引き続き、今回もOS付属のシェルスクリプトを読んでいく。「本当にこれでいいのか?」と思うような読みにくい記述も見つかるが、よく読むとシェルスクリプトならではの流儀を学ぶことができる(編集部)

[後藤大地,オングス]

とにかく何か作ってみろ

 シェルスクリプトはお世辞にも読みやすいプログラミング言語とは言えない。ほかの言語にはない特有のルールがあるので、あまり経験がない人がちょっと複雑なシェルスクリプトを読むと嫌になってしまうかもしれない。

 その半面、シェルスクリプトにはちょっと書いてすぐに試せるという良い点がある。シェルスクリプトの解説書やソースコードを読みながら「なんだかよく分からない」「マニュアルを読んでもよく分からない」という人には、短くてもいいからとにかく何かシェルスクリプトを書いて、試してみることを強くお勧めする。

 これはどんなプログラミング言語にも言えることだが、習得しようと思っても、解説書を読んでいるだけでは何も始まらない。どんな形でもいい、とにかく何かプログラムを作ってみないことには道は開けない。

 当たり前の話だが、プログラムを作ってみないことには、自分が作るプログラムにどういう問題があるのか知ることもできない。最初に作るプログラムは、短くても良い。見た目が悪くても良い。エラーになっても良い。そういう問題は、作った後でいろいろ調べて解決していけば良いのだ。

 「とにかく作って試してみる」ということを考えると、シェルスクリプトの「ちょっと書いてすぐに試せる」という特長は、学習者にとって好都合だ。書いて、試して、問題があったら調査して修正というサイクルを繰り返し、書き方を体で覚えてほしい。

 そして、第3回で説明したように、スキルのある人のコードを見て、技術を盗むことも大切だ。最初は何もできなくても、とにかく作ってみること。ある程度書き方を覚えてきたら、人の技術を盗むこと。シェルスクリプトに限らず、プログラミングを覚えるには、この2点を実践することが何よりも大切だ。解説書を読んでいるだけでは、スタートラインにも立っていないということだ。

前回に続き、OS標準添付のスクリプトから学ぶ

 前回予告したように、しばらくはFreeBSDに付属するシェルスクリプトを題材に、便利に使えるテクニックを紹介していく。前回は「/sbin/dhclient-script(dhclient-script(8))」のスクリプトから、いくつかのテクニックを紹介した。

 今回は/sbin/dhclient-scriptスクリプトの残りの部分を見ていく。さらに、教材を「/sbin/nextboot(nextboot(8))」に変えて、いくつかのテクニックを読み解いていこうと思う。

 dhclient-script(8)は、DHCPクライアントdhclient(8)が呼び出す設定用のスクリプト。dhclient(8)が必要とするネットワークインターフェイスの初期設定やアドレスのチェックなどの機能を持つ。

 nextboot(8)は、次の起動時に使用するカーネルを指定したり、起動オプションを指定するためのコマンドだ。「次のシステム再起動時はシングルユーザーモードで起動したい」とか、「次のシステム再起動ではGENERICカーネルで起動したい」という場合に活躍する。

その変数は空なのか?

 シェルスクリプトでは「変数が格納している文字列の長さが0であるか否か」という判定処理を記述することが多い。例えば、dhclient-script(8)を探すと、次のような記述が見つかる。

if [ -n "$new_domain_search" ]; then
        echo "search $new_domain_search" >>$tmpres
elif [ -n "$new_domain_name" ]; then
        echo "search $new_domain_name" >>$tmpres
fi

 上の例では、「変数new_domain_searchが格納している文字列の長さが0でなければ」と「変数new_domain_nameが格納している文字列の長さが0でなければ」という判定を記述してある。

 前回説明したように、[test(1)コマンドと同じ役目を果たす。[以降の内容を評価し、結果が真なら0(true)を、偽なら1(false)を返す。

 [を使って文字列を評価するときによく使うのが、オプション「-n」とオプション「-z」だ。-nは、後で指定する変数が格納する「文字列の長さが0でなければ」という意味になる。一方、-zは後で指定する変数が格納する「文字列の長さが0ならば」という意味だ。つまり、-nと-zで文字列を格納した変数が空であるか否かを判定できる。どちらも、何を意味しているのか忘れがちだが、-nが「Not Zero」で、-zが「Zero」の略だと覚えるといいだろう。

 勘が良い人なら分かると思うが、-nと-zは、どちらを使っても同じような意味のスクリプトを記述できる。どちらを使うかは、作成者の好みや癖によるところが大きい。ただし、意味を反対にとらえてしまいやすいところに注意してほしい。反対にとらえてしまうと、大混乱に陥るということはお分かりいただけるだろう。

 オプションの意味が分からなくなったら、その都度マニュアルを引いて確かめるようにしたい。[で利用できるオプションは、test(1)のオンラインマニュアルで確認できる。見た目は奇妙なことになるが、「man [」でも調べられる。

そのファイルは存在しているのか?

 次は、任意のファイルを指定して、そのファイルが存在するか否かを判定する例をお見せする。この判定もシェルスクリプトではよく使うものだ。ファイルの存在判定にはオプション「-f」を使う。次のサンプルはdhclient-script(8)からの引用だ。

if [ -f $tmpres ]; then
        if [ -f /etc/resolv.conf.tail ]; then
                cat /etc/resolv.conf.tail >>$tmpres
        fi

 -fは、後で指定するファイルが「存在し、かつ、それが通常のファイルであるかどうか」を判定させるオプション。上の例では、「/etc/resolv.conf.tail」というパスを引数として与え、これが通常のファイルであるかどうかを判定させている。

 ファイルの判定に関係するものとしては、ほかにオプション「-e」とオプション「-x」を使うことが多い。-eは、後で指定するファイルが「存在するかどうか」だけを判定させるもので、-xは後で指定するファイルが「存在し、かつ、実行可能か否か」を判定させる。そして、オプション「-d」を使って、パスを指定すると、そのパスが「ディレクトリであるか否か」を判定できる。

caseを使って柔軟なパターン指定

 次に挙げるサンプルも、dhclient-script(8)からの引用だ。ちょっと複雑だが読んでみてほしい。

case $resolvconf_enable in
# "no", "false", "off", or "0"
[Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0)
        if [ -f /etc/resolv.conf.save ]; then
                cat /etc/resolv.conf.save > /etc/resolv.conf
        fi
        ;;
*)
        /sbin/resolvconf -d ${interface}
        ;;
esac

 シェルスクリプトのcase構文では、パターンを記述することができる。サンプル中の「[Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0」という部分に注目してほしい。その上の行にあるコメントにも書いてあるが、これで「no」「false」「off」の3語と、数字の「0」が存在するか否かを判定できる。しかも、つづりが大文字でも、小文字でも、大文字と小文字の混合(どのような形で混合になっていてもよい)であっても判定できるのだ。上に挙げた例では、変数$resolvconf_enableの中身が「no」「false」「off」「0」のいずれかであるならば、その先のif文のよる条件判定に進むようになっている。

 パターンは「|」で区切って指定する。パターンの中では、さまざまな便利な機能を利用できる。例えば、絶対パス指定に替えて「~」を指定することで、ホームディレクトリへの移動を可能にする「チルダ展開」や、変数を指定して、その中身をパターン指定に使わせる「変数展開」、「$(コマンド)」でコマンドの実行結果をパターン指定に使わせる「コマンド置換」といった機能を利用可能だ。

 ほかにも、「$(演算)」で数値演算の結果を利用させたり、シングルクオートやダブルクオートやバックスラッシュを削除させる「クオート削除」、ワイルドカード指定(*)などの機能を利用できる。

 caseはこのようにシェルスクリプトで利用できる機能としては、一般的なプログラミング言語に似ているところが多く、応用の幅が広い。このため、if [ ]ではなくcaseを多用する開発者もいる。caseを使ってこのように記述したコードはあちこちで見かけるので、ぜひとも覚えておきたい。

 以上のように、dhclient-script(8)コマンドだけを見ても、さまざまなテクニックを使って記述してあることが分かる。続いて、nextboot(8)を読みながらテクニックを探ってみよう。

エラーメッセージだけを捨てる

 nextboot(8)から、最初に紹介したいのは次のコードだ。特に末尾にある「2>」という記述に注目してほしい。これもシェルスクリプトの基本機能であり、シェルスクリプトマスターが最もよく使う書き方の1つだ。

if cmp -s $tmpres /etc/resolv.conf; then
        rm -f $tmpres
        return 0
fi 2>/dev/null

 「2> /dev/null」と記述すると、「標準エラー出力(ファイルディスクリプタ2)を/dev/nullへリダイレクトする」ことになる。つまりエラーメッセージだけを捨てる、ということになる。

 ここではエラー出力を捨てる例を紹介しているが、書き方をちょっと変えればエラー出力だけをログファイルに出力させることもできる。「2> /pathto/log」と記述すればよいのだ。この手法もよく使うので覚えておきたい。

       1|2 次のページへ

Copyright© 2017 ITmedia, Inc. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。