OSに付属するシェルスクリプトを読んで技術を盗む:スマートな紳士のためのシェルスクリプト(3)(1/2 ページ)
今回から、OS付属のシェルスクリプトを読んでいく。多くの人が使っているスクリプトを読むことで、シェルスクリプトならではの書き方、テクニックを身に付けることができるはずだ(編集部)
他人の技術を盗まなければ進歩はない
外国語をマスターするにも、楽器の演奏を覚えるにも、上達するにはただ練習するだけではダメだ。素晴らしいお手本を見つけて、よく観察し、何度もまねることが必要だ。お手本から技術を「盗む」ことが大切だということだ。
プログラミングでも同じことが言えると思う。文法を覚えて、ただひたすらプログラムを書くだけではなかなか上手にならない。スキルのある人のコードを見て、技術を盗もう。開発チームのメンバーそれぞれが書いたコードを持ち寄って、お互いに批評し合う「コードレビュー」に参加している、あるいはリーダーとして主催しているという人は多いと思う。このコードレビューも、人から技術を盗む良い機会と言えるだろう。
今回からは、OSに付属するシェルスクリプトを読み解いて、技術を盗んでいこうと思う。作業環境には、Vim連載の第8回で紹介したPC-BSD 9.0を使用する。
教材は100本!
それでは、どのスクリプトを読むかを決めよう。FreeBSDのユーザーランドプログラムとしてインストールされているシェルスクリプトがどれくらいあるのか調べてみる。コンソールを開いて、「find」コマンドを利用して検索すると、以下のように100本もあることが分かる
# find /bin /sbin /usr/bin /usr/sbin /libexec /usr/libexec/ -exec file '{}' ';' | grep shell /sbin/dhclient-script: POSIX shell script text executable /sbin/mount_auto: POSIX shell script text executable /sbin/nextboot: POSIX shell script text executable /sbin/resolvconf: POSIX shell script text executable /usr/bin/cvsbug: POSIX shell script text executable /usr/bin/neqn: POSIX shell script text executable /usr/bin/grog: POSIX shell script text executable /usr/bin/nroff: POSIX shell script text executable /usr/bin/psroff: POSIX shell script text executable /usr/bin/rcsfreeze: POSIX shell script text executable /usr/bin/send-pr: POSIX shell script text executable /usr/bin/sendbug: POSIX shell script text executable /usr/bin/krb5-config: POSIX shell script text executable /usr/bin/alias: POSIX shell script text executable /usr/bin/bg: POSIX shell script text executable /usr/bin/cd: POSIX shell script text executable /usr/bin/command: POSIX shell script text executable /usr/bin/fc: POSIX shell script text executable /usr/bin/fg: POSIX shell script text executable /usr/bin/getopts: POSIX shell script text executable /usr/bin/hash: POSIX shell script text executable /usr/bin/jobs: POSIX shell script text executable /usr/bin/read: POSIX shell script text executable /usr/bin/type: POSIX shell script text executable /usr/bin/ulimit: POSIX shell script text executable /usr/bin/umask: POSIX shell script text executable /usr/bin/unalias: POSIX shell script text executable /usr/bin/wait: POSIX shell script text executable /usr/bin/cpasswd: POSIX shell script text executable /usr/bin/gzexe: POSIX shell script text executable /usr/bin/zdiff: POSIX shell script text executable /usr/bin/zforce: POSIX shell script text executable /usr/bin/zmore: POSIX shell script text executable /usr/bin/znew: POSIX shell script text executable /usr/bin/zcmp: POSIX shell script text executable /usr/bin/lesspipe.sh: POSIX shell script text executable /usr/bin/zless: POSIX shell script text executable /usr/bin/bzless: POSIX shell script text executable /usr/bin/xzless: POSIX shell script text executable /usr/bin/lzless: POSIX shell script text executable /usr/bin/lorder: POSIX shell script text executable /usr/bin/man: POSIX shell script text executable /usr/bin/apropos: POSIX shell script text executable /usr/bin/manpath: POSIX shell script text executable /usr/bin/whatis: POSIX shell script text executable /usr/bin/mkdep: POSIX shell script text executable /usr/bin/pagesize: POSIX shell script text executable /usr/bin/shar: POSIX shell script text executable /usr/bin/clear: POSIX shell script text executable /usr/bin/unifdefall: POSIX shell script text executable /usr/bin/vgrind: POSIX shell script text executable /usr/bin/yyfix: POSIX shell script text executable /usr/bin/lp: POSIX shell script text executable /usr/sbin/dtruss: POSIX shell script text executable /usr/sbin/adduser: POSIX shell script text executable /usr/sbin/rmuser: POSIX shell script text executable /usr/sbin/bsdinstall: POSIX shell script text executable /usr/sbin/crashinfo: POSIX shell script text executable /usr/sbin/freebsd-update: POSIX shell script text executable /usr/sbin/manctl: POSIX shell script text executable /usr/sbin/mergemaster: POSIX shell script text executable /usr/sbin/ndisgen: POSIX shell script text executable /usr/sbin/pc-sysinstall: POSIX shell script text executable /usr/sbin/periodic: POSIX shell script text executable /usr/sbin/portsnap: POSIX shell script text executable /usr/sbin/service: POSIX shell script text executable /usr/sbin/spkrtest: POSIX shell script text executable /usr/sbin/ypinit: POSIX shell script text executable /usr/sbin/zzz: POSIX shell script text executable /libexec/resolvconf/libc: POSIX shell script text executable /libexec/resolvconf/dnsmasq: POSIX shell script text executable /libexec/resolvconf/named: POSIX shell script text executable /libexec/resolvconf/pdnsd: POSIX shell script text executable /libexec/resolvconf/unbound: POSIX shell script text executable /usr/libexec/bsdinstall/auto: POSIX shell script text executable /usr/libexec/bsdinstall/adduser: POSIX shell script text executable /usr/libexec/bsdinstall/checksum: POSIX shell script text executable /usr/libexec/bsdinstall/config: POSIX shell script text executable /usr/libexec/bsdinstall/docsinstall: POSIX shell script text executable /usr/libexec/bsdinstall/hostname: POSIX shell script text executable /usr/libexec/bsdinstall/jail: POSIX shell script text executable /usr/libexec/bsdinstall/keymap: POSIX shell script text executable /usr/libexec/bsdinstall/mirrorselect: POSIX shell script text executable /usr/libexec/bsdinstall/mount: POSIX shell script text executable /usr/libexec/bsdinstall/netconfig: POSIX shell script text executable /usr/libexec/bsdinstall/netconfig_ipv4: POSIX shell script text executable /usr/libexec/bsdinstall/netconfig_ipv6: POSIX shell script text executable /usr/libexec/bsdinstall/rootpass: POSIX shell script text executable /usr/libexec/bsdinstall/services: POSIX shell script text executable /usr/libexec/bsdinstall/time: POSIX shell script text executable /usr/libexec/bsdinstall/umount: POSIX shell script text executable /usr/libexec/bsdinstall/wlanconfig: POSIX shell script text executable /usr/libexec/lpr/ru/bjc-240.sh.sample: POSIX shell script text executable /usr/libexec/save-entropy: POSIX shell script text executable /usr/libexec/locate.updatedb: POSIX shell script text executable /usr/libexec/locate.mklocatedb: POSIX shell script text executable /usr/libexec/locate.concatdb: POSIX shell script text executable /usr/libexec/makewhatis.local: POSIX shell script text executable /usr/libexec/catman.local: POSIX shell script text executable /usr/libexec/yppwupdate: POSIX shell script text executable #
これだけでも相当な数だが、/etc/ディレクトリも検索対象に加えると、さらに200本以上のシェルスクリプトが見つかる。しかし、/etc/に存在するシェルスクリプトはrcNGのような、関数を集約したライブラリであることが多い。最初に取り上げるには難解だろう。ここではまず、先ほど検索した100本のシェルスクリプトを、1本目であるdhclient-scriptから読んでいき、お手本となる部分を取り上げて解説していく。
不確定要素は排除
dhclient-scriptはDHCPクライアントプログラムであるdhclient(8)が呼び出すスクリプトで、DHCPサーバにIPアドレスを要求する前に、ネットワークインターフェイスの初期設定を済ませる。アドレスを取得したら、それをチェックし、インターフェイスに設定するなどの機能を備える。
まずは以下のコード片を見てほしい。[は組み込みコマンドとして実装されているもので、コマンドのtest(1)と同じ役目を持っている。シェルスクリプトユーザーなら、当然のように使っている機能だろう。ちなみに、組み込みコマンドとなっているのは[だけ。]は、[の引数である。
if [ -x /usr/bin/logger ]; then LOGGER="/usr/bin/logger -s -p user.notice -t dhclient" else LOGGER=echo fi
[ ]内にある「-x」は、その後に指定するファイルが存在しており、かつ、それが実行可能であるなら真となるオプション指定だ。つまり、このサンプルでは「/usr/bin/logger」(logger(1))という実行可能なファイルが存在するならという条件を設定しており、結果次第でその後の処理を振り分けるようにしているのだ。
ここで注目したいのはlogger(1)の存在を確認する部分の書き方だ。次のように書いてしまう人は多いと思う。
if type logger > /dev/null 2>&1; then LOGGER="logger -s -p user.notice -t dhclient" else LOGGER=echo fi
組み込みコマンドであるtypeを使って、コマンドの存在を確認するのは、シェルスクリプトを作るときにはよくやることだ。このサンプルではファイル名を指定して存在を確認している。typeが引数としてファイル名を受け付けるからだ。
しかし、先に挙げた例では、絶対パスでファイルを指定して、コマンドが存在するかどうかを調べている。これはあいまいな部分を排除するという狙いがある。
Ports CollectionやPBI、make installなどでユーザーがインストールするサードパーティ製アプリケーションとは異なり、ユーザーランドに最初からインストールされるスクリプトは、ファイルの場所が決まっている。
組み込みコマンドtypeを使って、「logger」というプログラムが存在するかどうかを調べるとしよう。この場合、環境変数PATHの影響を受けて、/usr/bin/loggerが検索対象から外れることがある。例えば、/usr/local/bin/loggerの存在を確認してしまう可能性があるのだ。
シェルスクリプトの動作は環境変数の影響を大きく受ける。スクリプトを作成した環境ではきちんと動いていたが、実行するユーザーが変わったり、実行する環境が変わったりすると動かなくなるということはよくある。OSに付属するシェルスクリプトでは、なるべく自由に解釈できる部分を排除して、問題が発生しにくい仕組みにするという考え方をよく反映している。
Copyright © ITmedia, Inc. All Rights Reserved.