1998年、さくらインターネットの黎明期、自分たちはまだ自社データセンターを持っておらず、テレウェイ(今はKDDI)のラックを借りてサーバ事業を展開していた。当時はデータセンターという言葉は一般的でなく、電算機センターなどと呼ばれていた。サーバの設置をするためには、入局届をFAXで提出し、決められた時刻に身分証明書を持って行かなければならない。まあそういう厳しい手続きは仕方がない。困るのは、センターがラックと回線以外は何も貸してくれないということだった。たとえばサーバを設置するに当たっては、ディスプレイやキーボードやネジ留めのためのドライバーが必要になるが、そういうものはすべて持ち込まなければならなかった。当時のディスプレイはCRTが一般的で、液晶はまだ高かった。お金のないベンチャーとしては、1立方メートル当たり何万円もかかるラックの中に、普段使わない物を置いておけず、作業のたびにえっちらおっちらディスプレイを運び込んでいた。そして自分たちがラック貸し出し事業をやるんだったら、必ずディスプレイは無償で貸すことにしようと誓い合った。そういうわけでさくらインターネットのデータセンターには、そこかしこに貸し出し用のディスプレイとキーボードと、工具セットが置いてあるのだ。
あるとき、新しいサーバを追加することになった。これをデータセンターへ運ぶのだが、いい加減ディスプレイを持ち込むのは面倒なので避けたい。そこでネットワーク接続までのセットアップは自宅で済ませておき、ネットに接続したら sshで繋いで作業しようと考えた。そうすればノートPC(これもネットに直結する)だけでセットアップができる。サーバを抱えてデータセンターへ行き、UTPケーブルを差して電源を入れ、ノートPCからpingを打ってみる……が、なんとpingに応答しない。どうやらconfigを間違ってしまったらしい。手元にはPS2のキーボードはあった(隙間に差し込めたのでラックに置きっぱなしにできた)。しかしディスプレイの代わりになるものがない。ノートPCにはディスプレイの入力端子がないので、外部ディスプレイとしては使えない。ディスプレイを取りに戻るにしても、車で片道1時間かかる場所なのだ。これは大変なことになった……と思ったが、試せることを試そうと考え直し、ディスプレイなしでキーボードをつないだ。そしてlogin:
と出ているだろうと想像して、アカウントとパスワードを入力してみた。
直面している問題はこうだ。間違ったのは、/etc/rc.localの中でifconfigに与えるIPアドレスだろう。当時のFreeBSDのconfigは、だいたいこんなようになっていた。
network_interfaces="fxp0 lo0" ifconfig_fxp0="inet 192.168.65.102 netmask 255.255.255.0" defaultrouter="192.168.64.1" hostname="unicorn.srs.ne.jp"
まずこれを修正したいが、どんな風に間違えたかはディスプレイがないので確認のしようがない。なのでこの行を丸ごと全部打ち直すしかない。普通のエディタだと、ディスプレイなしでは問題行を削除して打ち直すのは曲芸に近いが、自分には目算があった。edを使うのだ。コマンドプロンプトが出ていると信じて、edを起動してこんな風にタイプした。
ed /etc/rc.local 146 /ifconfig ifconfig_fxp0="inet 192.168.65.102 netmask 255.255.255.0" c ifconfig_fxp0="inet 192.168.64.102 netmask 255.255.255.0" . w 146 q
ハイライトしたところが、edの出力だ。edの出力は元々暗号のようで役に立たないので、ディスプレイがなくてもギリギリなんとかなる。フィードバックはゼロだが、うまくいっていると信じてリブートした。そしてまた外からpingを打ってみると、今度は無事に届いた。めでたしめでたし、である。
ed – ラインエディタ
edは非常に古いエディタだ。なにしろUnixの誕生と共に作られたというのだから本当に古い。edはラインエディタと呼ばれる種類のテキストエディタで、テレタイプという、キーボードとタイプライタが魔合体したような奇妙な機械で使うことを想定している。そう、1960-1970年代というのはCRTですら贅沢品だったのだ。テレタイプには画面というものはなく、出力は紙に印字され、改行されていく。そもそもカーソルの概念がないので、文字をポイントすることができない。edはインクがもったいないと思ったのか、エラー表示すら「?」だけで済ませるほどだ。
ちなみにテレタイプでUnixを使うとどんな具合かは、Youtubeで検索すると動画がヒットするので参考にご覧いただきたい。たとえばこんな感じだ。
ラインエディタの基本
edのようなラインエディタは、注目行あるいはカレント行という考え方を用いる。注目行を指定する第1の方法は、行番号を指定するやり方だ。たとえば
3
と入力すると、注目行が3行目に移動する。/ifconfig/
(末尾のスラッシュは省略可能だ)は文字列を検索し、最初にヒットした行に注目行をセットする。従って
/ifconfig
と入力すると、目標の編集したい行に注目行が移動する。
edのコマンドは、多くが1文字で構成されている。たとえばd
はdeleteの略で、注目行を削除する。i
はinsertの略で、注目行の前に入力を追加する。c
はchangeで、注目行を丸ごと打ち直すときに使う。もうひとつ、注目行の後に入力するa
つまりaddがある。a
やi
やc
は、ピリオド.
だけの行を入力するまで、入力モードが続く。
c ifconfig_fxp0="inet 192.168.64.102 netmask 255.255.255.0" .
w
はwriteで、ファイルを書き込む。 表示されるのはファイルのバイト数だ。 q
はquitで、edを終了する。ちなみにファイルを保存せずに強制終了するときはQ
だ。
edはラインエディタなので、文字を修正しようとすると非常に面倒なことになる。変更したい文字をポイントできないからだ。なので、文字の修正のために置換機能を使わなければならなくなる。上述の例なら、ミスタイプした65を64に書き換えたい。なのでs
コマンド(substitute)を使ってこう編集することになる。以下はディスプレイがあって、間違いが明確な場合の修正方法を示したものだ。
/ifconfig s/65/64/
s/パターン/置換文字列/
は、他のコマンドと同じく注目行に対して実行される。
edは、パターンに正規表現が利用できるという点でも大変優れている。そもそもedはUnixコマンドで一番最初に正規表現を実装したプログラムだ。ただ、それゆえedの実装している正規表現は一番原始的でもある。現在の、たとえばPythonなどが提供する拡張正規表現は使えない — と思ったら、Linuxの現在のedでは拡張正規表現が使える! gnu regexで実装しているようだ。そりゃそうか。
アドレスとコマンド
さて、edをインタラクティブに使うときには、注目行を移動させるアドレスと、注目行を編集するコマンドがあることを説明した。実はこれらは1行で書くこともできる。edのコマンドの形式は次の通りだ。
[アドレス[,アドレス]]コマンド[パラメータ]
アドレスは行を指定する。行番号を指定したり、/パターン/
と書けばパターンを含む行を指定することができる。カンマで区切ると範囲も指定できる。たとえば1,10
と書くと「1行目から10行目」という意味になるし、/abc/,/xyz/
とすると「abcを含む行からxyzを含む行まで」という意味になる。アドレスには特別な意味を持つ記号もある。$
を指定すると最後の行という意味になる。したがって1,$
でバッファ全体を指定できる。これを省略して,
または%
で代用することもできる。
アドレスとコマンドは1行で組み合わせて書くことができる。従って、先ほどのIPアドレスの修正なら、1行でこう書ける。
/ifconfig/s/65/64/
これは「ifconfig
というパターンを含む行で、s
コマンドを実行せよ」という意味になる。もし、ファイルの中で「65」という文字列がユニーク(他に登場しない)ならば、もっと短くこうできる。
/65/s//64/
s
コマンドは、検索パターンを省略すると直前のアドレスのマッチをそのまま使いまわしてくれるので、これで狙った文字列を訂正できるのだ。ただ繰り返すが、これはディスプレイがあってファイルの内容があらかじめ分かっているときしか使えない。
s
コマンドは行の中で変換を1回しか実行しない。複数のマッチを全部置換したいときは、末尾にg
(global)を付ける。
アドレスにはもうひとつ便利なトリックがある。g/パターン/
と指定すると「パターンを含む行すべて」が対象になるのだ。もしもっと大きなファイルを編集しているとして、「すべてのifconfigを含む行で置換したい」と思えば、こんな風に書ける。
g/ifconfig/s/65/64/
さて、edは正規表現をサポートした最初のコマンドだと述べた。つまりパターンとはRE(Regular Expression)だ。これに注目行をプリントするコマンドp
を組み合わせると、こんなコマンドが出来上がる。
g/RE/p
これは「すべてのREにマッチする行を表示する」という意味になる。
edは標準入力からコマンド列を与えるとバッチ的に使える。このため、こんな風に使うこともできる。
echo 'g/ifconfig/p' | ed rc.local 146 ifconfig_fxp0="inet 192.168.64.102 netmask 255.255.255.0"
これをバッチ化すれば便利だ。だが、こんなことのためにいちいちedを起動するのもアレな話なので、この機能を単体で切り出そうということになる。では名前をどうするか? 当然grepということになる。
edのアドレス指定では、v/パターン/
という記述もできる。これはパターンを含まない行(invert)にマッチする(grepの-v
オプションに相当する)。自分は「パターンを含まない行を削除する(パターンを含む行だけを残す)」という意味で
v/RE/d
をよく使う。
edファミリー
edには派生プログラムがたくさんある。grepについてはすでに述べた。ほかのプログラムについても触れてみよう。
edにコマンドを突っ込むというアイディアを発展させたのがsedだ。sedはストリームエディタで、edのような対話型ではなく、コマンドをスクリプトっぽく与えることで、パイプライン(標準入力を処理し、標準出力へ吐き出す動作)を効率よく処理することを目的としたプログラムだ。ファイル全体を走査し、置換や加工をすることに長けている。
edもコマンド列を標準入力から食えるようになっているが、sedはスクリプトの概念をもう一歩進めたような感じになっている。sedは1行ずつ読み込んでコマンドを実行する疑似的なループを形成し、これにスクリプトを適用していく。sedのコマンド形式はedと同じように作られている。コマンドの多くも互換性があるが、一部は拡張されているし、edにないコマンドも追加されている。
sedの一番簡単でもっともよく使われる例は文字列の置換だ。さくらのクラウドでスタートアップスクリプトを連動・整備する の回でもいくつか例を示した。上述したrc.localの修正をsedで(強引に)やるとこうなる。
sed -i '/ifconfig/s/65/64/' rc.local
コマンドの意味はedと同じなので理解していただけるだろう。オプション-i
は、ファイルを読み込み、処理を施してから結果で上書きすることを示す。sedはパイプラインで使うことを想定しているので、そのままでは標準出力に結果をプリントしてしまう。-i
オプションを指定すると、入力ファイルをそのまま結果で置き換えてくれる(割と最近の拡張オプションで、これがない時代にはいったんテンポラリファイルに書き出してからmvするしかなかった)。
sedは、デフォルトでは1行ずつ入力を読み、そのまま出力するようになっているが、-n
オプションで出力を抑制することができる。このときは、p
コマンドで明示的にプリントするまで何も出力しない。またsedにはデフォルトループによらず1行先読みするコマンドや、ホールドスペースという拡張もあって、これを使って凝った処理ができるようになっているが、実のところawk以降のスクリプト言語ほどの自由度はないので、あまり真剣に学ぶ必要はない。
exはBill Joyが作ったedの互換プログラムだ。つまりラインエディタなのだ。exはed互換だが、edよりも多くのコマンドが拡張されていたり、特定のコマンドがなくなっていたりする(あまり互換性はない)。たとえばexは、プロンプトに:
を出力する。強制終了はQ
ではなくq!
だ。しかし基本的なコマンドはだいたい同じだ。アドレスとコマンドの組み合わせはそのままだし、一部は便利に拡張されている。
viはexのスーパーセット、すなわちラインエディタをスクリーンエディタに拡張して作られた(exのvisualモードなのでviという名前になった)。 viの強いモード指向は、exのラインエディタからの影響もあるのではないかと思われる。i
やa
を使って入力モードへ移行し、ESC
で戻ってくるという考え方はed/exの入力モード(ピリオドで戻ってくる)そのままの考え方だ。
自分がedをある程度使えるのは、普段からedを使っているわけではなく、exモードである程度コマンド体系を把握しているからだ。ラインエディタの概念を把握していなかったら、edをいきなり起動するなんてことは到底不可能だと思う。
強引なまとめ
viとexは表裏一体なので、viを上手に使いこなすにはexコマンドに慣れ親しんでいなければならない。これは要するに、edファミリーに親しむということで、結局Unixの基本コマンドに直結するということになる。現代においては、edを起動する機会はまったくない。viのexモードで完全に代替できるからだ(ディスプレイを忘れない限り!)。しかしedのコマンド体系に習熟していないと、結局はexモードを使いこなせないことになり、viの真の魅力を引き出せないということになってしまう。
grepやsedはどうだろう。grepは今でもよく使うと思うのだが、sedはそれほどでもないかもしれない。/etcの下のスクリプトをちょっと調べてみたが、
[root@vortex etc]# grep -l -w -R sed * | wc -l 61 [root@vortex etc]# grep -l -w -R grep * | wc -l 95
ということで、それなりに使われているようだ。ただ用法としては、sedは単なる置換ばかりだった。まあ現代ではperlやpythonが使い放題なのだから、いまさらsedで凝ったスクリプトを書くことはないと思う。