2012/08/23

VMware Fusion 5 リリース、でも、買うのはちょっと待った方が

本業に追われてだいぶご無沙汰してしまった。

さて、米国時間で 8/23に、無事 VMware Fusion 5 と VMware Workstation 9 がリリースされた。

http://www.vmware.com/products/fusion/overview.html

Fusion 5 では Windows8 への最適化など新しい機能がある。また、私自身はまだ確認していないが、Fusion 4 に比べ複数の仮想マシンを実行した際のメモリ消費が抑えられているという体験談を聞いている。vSphere にある透過的メモリ共有に類する技術を使っているのか、はたまたこれまで無駄に消費してたのがダイエットしただけなのかは不明だが、確認を取ってみたいところだ。

また、Professional Edition ができたのも1つの特徴だ。
http://www.vmware.com/products/desktop_virtualization/fusion/professional.html

ネットワークエディタは Workstation ではおなじみだが、vmnet0-8 までのネットワークに任意の設定をする事が可能になるというものだ。
Fusion ではこれまで vmnet0 をブリッジ、vmnet1 をホストとの通信だけが可能なネットワーク、vmnet8 をNATを行うネットワークとして使用してきた。が、あいている vmnet2-7 に任意の設定のネットワークを作ってNATや仮想マシン間ネットワークを個別に用意できる。また、vmnet1 や vmnet8 のDHCPで配布されるIPレンジを変更なども可能になる。

制限された仮想マシンを作成できるらしいのもポイントだ。VMware Workstation ではかつて VMware ACE という拡張機能があり、暗号化され利用者が設定を変更のできない仮想マシンを作ることができた。暗号化自体は Fusion 4 よりサポートされていたが、より細かい制約が作れるようだ。

管理者が制約を作り、利用者に仮想マシンを配布する、といった用途が期待できる。

価格の方は、VMware Store で Fusion 5 が $49.99、Fusion 5 Professional が $99.99 だ。Fusion 5 へのアップグレードはなく買い直しになる模様。

一方、 Fusion 5 Professional については Fusion 3,4 からのアップグレードパスがある。こちらも $49.99 なので、すでに VMware Fusion を使ってる方は Professional 版にして細かい機能を楽しんでみるのもいいだろう。

また、Fusion 2012 TechPreview を使ってきたβテスターには、後ほどプロモーションコードが配布されるようだ。このパブリックベータでテストしてきたユーザは、購入を少し待った方がいいだろう。

なお、Fusion に限らず VMware 製品は、ダウンロードしてそのまま実行すると評価版として一定期間動作する。なので、すぐに買わなくてもしばらくは評価版として利用が可能だ。
業務で使うなど明確な商利用などの都合がない、パブリックベータを試してこれたような個人ユーザなら評価版で待ってても問題はないだろう。

2012/06/27

Macbook Air で Windows XP を使う場合のライセンス

少々厄介な話になったので少しメモしておく。

Macbook Air や、今回発表になった Macbook Pro Retina ディスプレイモデルでは USB や Thunderbolt といったいくつかのインターフェイスを除いて拡張の余地がない。
たとえばメモリを増設したり、HDD/SSD を交換することが原則的にできなくなっている。


このため、 DSP版の Windows を購入することがほぼ不可能になっている。

本来ホワイトボックスPC向けに用意されている DSP版の Windows は安価な一方、サポートは販売店持ちとなり、かつ特定ハードウェアとの同時購入でそのハードウェアを使用しているコンピュータでの利用のみが許される。


特定のハードウェアは何でも良いという訳ではない。たとえば以下は DOS/V パラダイスのページだが、バンドル対象製品は、CPU、メモリ、HDD、SSD、光学ドライブ、マザーボード、ビデオカード、その帆は拡張カードと言う感じで「本体に組み込まれるもの」が前提となっている。
http://www.dospara.co.jp/5info/share.php?contents=info_win_dsp

USB接続のHDD など、本体を構成しない外付け機器やケースなどは対象外となっている。


これまで、巷で販売されている Windows ライセンス付きの VMware Fusion 製品は、その多くが「DSP版とのバンドル」で、よく見るとメモリなどがセットに含まれている。
ライセンス上は、そのメモリを使用した MacやPCでのみ、その Windows ライセンスは使用できる。


まあもちろん、一々そのメモリ(ないしは他の特定ハードウェア)を搭載しているかを確認する術はないし、確認して販売しているかは微妙、だろう。



しかし、Macbook Air、 Macbook Pro Retina ディスプレイモデルはそもそも拡張の余地がない。「できない」所に販売するのはさすがにできないだろう。


Macbook Air や、今回発表になった Macbook Pro Retina でぃすぷれいもでるといった本体と同時に Windows ライセンスを購入」しない限り、後から仮想環境や BootCamp 用の OSライセンスをDSP版で購入することはできない訳だ。
なお、聞いた限り銀座などの AppleStore では  DSP版の Windows は扱ってないようだ。



じゃあ、大人しくパッケージの Windows を買えばいいかというとこれも厄介な話がある。
なぜなら、パッケージの Windows にはダウングレード権がないからだ。

マイクロソフトの製品は、新バージョンが出ると旧バージョンの販売は終了してしまう。Windows7 が販売されている現在、Windows XP や Vista のパッケージはもう購入できない訳だ。(希に不良在庫がぽろっと出ることはあるが)

Windows7 を使いたい場合はそれでいいが、 仮想環境上で Windows XP を使いたい場合、ダウングレード権のないパッケージの Windows ではその役に立たないのだ。
(DSP版にはダウングレード権が存在する。)


あともう一つの方法はボリュームライセンスでの購入になる。ボリュームライセンスは法人利用を前提としたライセンスだけのまとめ買いシステムだが、別に個人が利用できない訳ではない。利用例は以下ページを参照してほしい
http://pc.watch.impress.co.jp/docs/topic/feature/20090911_314639.html

ボリュームライセンスで販売されるデスクトップ版の Windows は「アップグレード」のみで、フルライセンスは存在しない。つまりコンピュータにアップグレード対象となるOS が入っていないと利用できない。自作PCなどでOSなしで作られたPC にはインストールできない訳だ。

では Mac にも入れれないのでは?と思われるが、実は「アップグレード対象」には OS X も含まれるというのがポイント。これは以下ページに記載されている。

http://www.microsoft.com/ja-jp/licensing/about-licensing/windows7.aspx#tab=4


ボリュームライセンスの利用には最低でも3アイテムの購入が必要なのが少々厄介だが、それは先の impress  のページを見て対応してほしい。


まとめると、後で拡張ができない、DSP版の Windows が買えない Macbook Air や Macbook Pro Retina ディスプレイモデルでは、以下の手段での購入となる

  1. Macbook Air/Pro と同時にDSP版を購入する
  2. ボリュームライセンスの Windows 7 を購入する
  3. ダウングレード権を諦めて Windows 7 を使用する
  4. (開発/テスト/デモ限定)MSDN/Technet を利用する


2012/06/03

Mac で ESXi は動くのか?(2)

前回、Apple ハードウェアで ESXi が起動できるのは、公式には Xserve (Early2009)のみ、非公式でも Xserve とほぼ同じチップセットを採用した MacPro のみ、と記載した。

しかし世の中すごい人はいるもので、とうとう Mac mini で ESXi の起動に成功したようだ。

以下にあるが、要はNICドライバーを追加してこれまで認識しなかった Mac mini のオンボードNICを認識させたようだ。

http://paraguin.com/2012/01/10/the-mac-mini-vmware-esxi-5-server-part-2-installation/

これは、実機を入手したら確かめてみたいと思う。

2012/05/13

Fusion で Mac OS X のカーネルデバッグ

● Kernel space is the last frontier 

...とまでは言わないが、しかしカーネルコードをみたりいじったりするのは非常に楽しい。一方で、カーネル空間でのミスは簡単にOSクラッシュを引き起こす。なんだかんだいって OSがなんとかしてくれるユーザランドでのアプリケーション開発やブラウザの上のスクリプト書きとは違う厄介さがある。

通常、カーネル内に組み込まれるコードのデバッグには2マシンデバッグが行われる。開発中のコードを実行するテストマシンとは別にデバッグ用のマシンを用意し、何らかの方法で接続、デバッグ用のマシンでデバッガを動かし観察するというものだ。接続方法は、昔ならシリアルケーブル、最近だと(少々癖はあるが) TCP/IP が利用できる。
もっとも、私はこれまで2マシンデバッグできる贅沢な環境でコードを書いたことがない。昔作った W-ZERO3用のシリアルドライバは、気合い一発の printf デバッグだった...、ファイルに書き出す前に落ちるので、WindowServer 殺してコンソールモードにしてログをはき出させ、死んだ瞬間の画面をメモって開発するという。今にしてみれば呆れた方法だった。

Mac OS X はネットワーク越しの 2マシンデバッグをサポートしている。方法については Mac OS X のドキュメントに記載があるので読んでみると良い。

とはいえ、2台もMacを横に並べて開発を行うのは少々面倒だ。仮想環境を使えばこの2マシンデバッグも簡単にできるのではないか?という事で試してみた。


● 仮想環境でカーネルデバッグを行うには

まず Mac Developer Program には入っておいた方がいい。年 $99 の有償プログラムだが、その価値はあるだろう。

開発環境である Xcode は無償でダウンロードできる。なので有償プログラムに入らなくてもアプリケーションや KEXT の開発は出来なくもない。ただし Xcode 以外の開発リソース、例えばカーネルシンボルなどその他配布物が有償登録せずに手に入るかは確認していない。


Fusion を使った2マシンデバッグの大枠の手順は、以下のページに既に記載されている。先の Apple のドキュメントとあわせて読んでおくといいだろう。
"Reverse Engineering Mac OS X : Mac OS X Kernel debugging with VMware"
http://reverse.put.as/2009/03/05/mac-os-x-kernel-debugging-with-vmware/
上記の記事は、まだ Lion のリリースされていない 2009 年のもののため、OS X のインストール方法が少々あやしいことを書いていたりするが、さくっと忘れてほしい。Lion の場合は インストールアプリケーションの下から InstallESD.iso さえ取り出せれば、細工せずとも普通にインストールが可能だ。

上記記事を参考に、私のやった手順を以下に記す。

以降、Xcode4 で開発を行い、ビルドを実行するホスト環境を Develop Machine、実際にコードを実行しテストを行うゲスト環境を  Target Machine と書く。


● ゲスト環境( Target Machine ) での準備

まず、仮想環境上に Mac OS X Lion をインストールする。

アップデートなどが終わったところで一度シャットダウン、停止状態でスナップショットをとっておく。今後何度も何度もクラッシュさせることになるので、安全な状態を確保しておくのは必要なことだ。

ゲスト環境のネットワーク接続は NAT のままで構わない。ただ、IPアドレスはDHCPではなく固定IPに書き換えておいた方が楽かも知れない。Fusion の DHCPサービスは概ね同じIPアドレスを割り当ててくれるが、変わると少々ややこしいことになるので気になる場合は固定IPに変更をしておくといい。

Develop Machine から Target Machine へファイルをコピーする手段を整えておく。ファイル共有を使ってもいいし、scp をつかっても良い。私は  Target Machine のアカウントの .ssh/authorized_keys に Develop Machine での自分のアカウントのSSHの公開鍵を入れておき、パスワードなしで簡単コピーできるようにしておいた。

続けて、カーネルデバッグ特有の設定を行う。

Apple のドキュメントだと NVRAM に引数を設定するようになっているが、仮想環境では nvram ではなく /Library/Preferences/SystemConfiguration/com.apple.Boot.plist に記載する。このファイルは管理者権限ではないと編集できないので、sudo  を使って vi なり nano なりで編集して欲しい。

デフォルトでは、
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Kernel Flags</key>
<string></string>
</dict>
</plist>
となっているのを
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Kernel Flags</key>
<string>-v debug=0x04 pmuflag=1</string>
</dict>
</plist>
に修正する。「-v」は verbose オプションで、起動時に黒画面に起動メッセージが表示されるようになる。

-v を指定すると、起動中のメッセージが表示される
さらにカーネルクラッシュした場合に、画面の上にコンソールメッセージを出して止まってくれる。一般ユーザには忌み嫌われるクラッシュ画面だが、カーネル開発をしているときにはこの断末魔の悲鳴は重要なヒントになる。

通常のクラッシュ時の画面
debug= でカーネルデバッグを指定した場合
MACアドレスとIPアドレスが表示されているのが分かる
-v オプションを指定した場合

debug=0x04 はデバッグオプションの設定で、Apple のドキュメントによると以下の通りだ

シンボル
フラグ値
意味
DB_HALT0x01OS起動時に一時停止を行い、デバッガーの接続を待つ
DB_PRT0x02コンソール画面に kernel 内の printf 出力を表示させる
DB_NMI0x04NMI (Non Maskable Interrupt) 信号でOSを一時停止しデバッガーに落とす
DB_KPRT0x08kprintf の出力をシリアルポートに出す
DB_SLOG0x10外部のGDBではなく組み込みのddb(kdb)をデフォルトデバッガにする
 (ただし、通常のDarwinカーネルにddb は組み込まれていない)
DB_ARP0x20デバッガに ARP テーブルの操作やルータを介した通信を許す(セキュリティーホールになるので注意) -- なお、どのカーネルにも本機能は含まれていない
DB_KDP_BP_DIS0x08古いGDBをサポートする
DB_LOG_PI_SCRN0x08グラフィカルなクラッシュ画面を表示させない

先の Reverse Engineering Mac OS X のドキュメントでは debug=0x01 を指定している。

 Target Machine の起動時に一時停止されるので、その間に Develop Machine の GDB で接続するというものだ。その理由として「仮想環境でどうやって NMI を起こせばいいか分からなかった(because I couldn’t find a way to create NMI events inside the virtual machine.)」とある。

確かに通常は「Command-Option-Control-Shift-Escape」か「Command-Power」キー操作でNMIが発生すると Apple のドキュメントにはあるが、仮想マシン内ではこれはうまく動かないようだ。

そこで Reverse Engineering Mac OS X では起動時に一時停止する手段をとったが、これだと起動のたびに GDB の atacch が必要になり面倒だ。そこで、強制的にデバッガを起動する KEXT 「PseudoNMI」を用意した。

ソースコードは GitHub に置いたので好きにみてほしい。
https://github.com/shiro-t/PseudoNMI

使い方は以下の通りだ。

  1. この PseudoNMI.kext をダウンロード
  2. パーミッションなどを調整する(所有権は root:wheel で、読み取りと実行のみに)
  3. sudo kextload ./PseudoNMI.kext」でカーネルに組み込む。
  4. sudo sysctl -w debug.pseudo_nmi=1」を実行する

sysctl を実行すると、内部的にNMIが発行されたのと同じ処理が動く。debug=0x04 が設定定されていれば外部デバッガーの接続待ちになり、そうでなければクラッシュする。


接続は TCP/IP を通じて行われる。ただし、そのままでは上手くいかないので、あらかじめ ARPテーブルに設定が必要だ。

ARP テーブルとは IPアドレスと Ethernet でのハードウェアアドレス(MACアドレス)を紐付けする対応表で、通常はカーネルが自動的に保守をしている。
カーネルは知らないIPアドレスへの通信が行われる際に ARP というプロトコルを使ってそのIPアドレスに対応するMACアドレスを手に入れている。その都度手に入れていたのではネットワークに負荷がかかるので、カーネル内のメモリに一定期間保管している。このARPテーブルの内容は「arp -a」コマンドで確認できる。

ARPによる処理は自動的に行われるので通常は気にする必要がない。ただ、デバッガ起動時はどうも自動的に手に入れた値を全てクリアしてしまっているようで、そのままでは通信できなくなる。そこで、あらかじめ固定的な値を組み込んでおく必要があるのだ。

手順としては、まず、Develop Machine 側の IPアドレスとMACアドレスを手に入れる。これは仮想マシンがどの方式でネットワークに繋がっているかによる。NAT の場合は vmnet8 というホスト側の仮想NICを通じて仮想マシンと繋がっている。このNATネットワークは 「192.168.x.0/24」のネットワークで、x の部分はインストール時に決定される。

そして、192.168.x.1 はデフォルトルータとして使われ、Develop Machine そのものの通信は 192.168.x.2 で行われる。
192.168.x.2 に対応する MAC アドレスは、Develop Machine  ifconfig vmnet8 を実行するか、 Target Machine 側で arp -a で ARP テーブルを表示させて確認できる。

IPアドレスと MAC アドレスの対が分かったら、 Target Machine 側で以下コマンドを実行する
sudo arp -s 192.168.x.2 aa:aa:aa:aa:aa:aa
これでOSが再起動されるまで削除されない ARPテーブルの設定がなされる。
仮想マシンのネットワーク構成を変えない限りこの値は一定のため、以下設定を /Library/LaunchDaemons におくことで起動の都度 自動設定させることも可能だ。


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>Label</key>
        <string>jp.nest.debug.static_arp</string>
        <key>ProgramArguments</key>
        <array>
                <string>/usr/sbin/arp</string>
                <string>-s</string>
                <string>192.168.203.2</string>
                <string>0:50:56:f6:cc:a8</string>
        </array>
        <key>RunAtLoad</key>
        <true/>
</dict>
</plist>

このファイルを /Library/LaunchDaemons に配置したあとでリブートしてもよし、「sudo launchctl load /Library/LaunchDaemons/jp.nest.debug.static_arp.plist」を実行して読み込ませても良い。

これで、ゲスト側の準備は完了だ。


● ホスト環境(Develop Machine) でのデバッグ設定

さて、ホスト側の設定にうつる。

まず、Kernel Debug Kit を手に入れる。Kernel Debug Kit はシンボル情報(バイナリ中のどこがどの名前の関数に対応するかなどデバッグに必要な情報)が一式整ったものだ。OS のバージョンごとに存在するため、Lion の場合 kernel_debug_kit_10.7.3_11d50.dmg を手に入れる。入手先は http://developer.apple.com/sdk だ。

ダウンロード後、ダブルクリックしてイメージをマウントしておく。

必要に応じてカーネルのソースコードも手に入れておいた方がいいだろう。

http://opensource.apple.com から適切なバージョンを選んで、xnu-<xnuのバージョン> を選択、ダウンロードする。Lion (10.7.3) の場合は「xnu-1699.24.23」がそれだ。tar.gz アーカイブで手に入るので適当なところに展開しておく。カーネルソースがあれば、デバッガでソースコードを参照できる。


次に、ゲストで行ったのと同じ ARP テーブルの設定をホスト側でも行う。ゲスト側のNIC(en0)のIPアドレスと MAC アドレスを割り出した後
sudo arp -s <IPアドレス> <MACアドレス>
を実行する。Develop Machine側 vmnet8 のIPアドレスは常に一定だが、ゲスト( Target Machine )側の en0 のIPアドレスは、デフォルトではDHCPのため変わることがありえるので注意だ。冒頭にも書いたように、変わるのが面倒な場合は固定IPアドレスにしてしまうといい。

ここで準備は終了だ。

● ターゲットとなる KEXT を準備する

カーネルデバッグを行う最大の理由は、KEXT のデバッグであろう。

Xcode で開発を行い、KEXTをビルドする。ビルドすると KEXT と同じところに .sym のついたそのKEXTのシンボルファイルが生成されるのでこれも取得しておく。

開発した KEXT を  Target Machine にコピーする。
コピー後には 「所有者とグループを root:wheel に」「グループとその他の書き込み権限を削除」を忘れないように。セキュリティ上の都合から権限が甘い KEXT は読み込まれないようになっている。

KEXT が正しくロードできるかについては、kextutil というコマンドで確認できる。
sudo kextutil Foo.kext
なお、私はこの kextutil でチェックしてる時にクラッシュしたことがある。
出来の悪い KEXT だとそういう可能性もあるので注意してほしい。


● カーネルデバッグを行う

Develop Machine 側で GDB を立ち上げる
gdb /Volumes/KernelDebugKit/mach_kernel
Kernel Debug Kit をマウントしていると、/Volumes 以下にマウントされているはずだ。
以下コマンドを入力、準備を行う
gdb$ target remote-kdp
以下のコマンドを入力だけ行い、リターンを押さずに置いておく

gdb$ attach <Target Machine のIPアドレス>

 Target Machine  側にうつって以下コマンドを実行、PseudoNMI が存在するのを確認する
sysctl debug.pseudo_nmi
正常ならば「debug.pseudo_nmi: 0」と返ってくるはずだ。
存在していれば以下を実行する
sudo sysctl -w debug.pseudo_nmi=1
すると、実行した瞬間にゲストOS上の Mac OS X の実行が停止される。
すかさず Develop Machine 側で入力だけしておいた attach コマンドを実行する。

ARP テーブルが固定化されており、ちゃんと通信が出来るなら即座に「Connected.」と表示されプロンプトが表示される。もしそうならない場合は、ARPテーブルの設定忘れなので改めて設定をおこなってほしい。

接続できたら「gdb> c」 と c(ontinue) コマンドを実行する。すると、止まっていたゲストOSの実行が再開される。

これでGDBが接続された状態になっている。あとはブレークポイントを設定するなり、クラッシュしたらスタックトレースを引っ張るなりいつものデバッグができる。


2012/04/29

Bhyve が Fusion 上で動いた




動いたので記念撮影。このあと起動し、ちゃんと root でログインできている。

細かいことはもう少し詰めてから書くが、ポイントは
  • 急がば回れ。多分今の -curent なら深く考えなくても Fusion 上で動くのでソースからビルドが結局は近道
  • http://bhyve.org にバイナリきっとあるけど、これそのままでは Fusion 上で動かない
  • 問題は現在の Bhyve のコードでは治っている
  • 要点? vhv ではVM_EXIT_SAVE_PAT と VM_EXIT_LOAD_PAT が非対応のため
というところだ。

適当に -current 引っ張ってきて手直しした vmm.ko をこちら においたので、もし試したい場合は以下の手順でいけるだろう。(要追試)

  1. (必要かはまだ絞り込めていないが)、とりあえず4GB はメモリを持った仮想マシンを作っておく
  2. FreeBSD 9.0R を普通インストールする
  3. http://bhyve.org にリンクされている bhyve-0.0.1r225757.tar をダウンロードする
  4. pkg_add <ダウンロードした bhyve-0.0.1r225757.tar> でインストールする
  5. /boot/kernel/vmm.ko を上記のパッチ済みのもので差し替える
  6. http://bhyve.org にリンクされているゲストイメージをダウンロードする。1GB/400MBどちらでもよい
  7. xz -cd <ゲストイメージ.tar.xz> | tar xf -  で展開する
  8. 出来たフォルダを # mv ./bhyve-guest-400mb /usr/bhyve-guest で /usr/ 以下に配置する(トップフォルダの名前をリネームするので要注意)
  9. /boot/loader.conf を作成、以下の情報を書き込む(2GBをホストに割り振る)
  10. hw.physmem="0x80000000"
    debug.witness.watch="0"
  11. 再起動して設定を生かす
  12. root で cd /usr/bhyve-guest から sh ./vmprep.sh でカーネルモジュールを読み込ませる
  13. sh ./vmrun.sh で仮想マシンを起動する

詳細なことはまた改めて書く。

2012/04/25

[余談] ファイルを探索後、そのファイルを検索する

ちと本日ネタになったので覚え書き代わりに書くとする。

本日みたある資料では、以下のような感じになっていた。
grep ERROR `find . -name "debug*.txt" -print`
これがいけないこと、というのを指摘したのだが分かってもらえなかったようなので何故いけないかをつらつら語りたいと思う。


ある程度 UNIXの経験がある人ならば「何だその話かよ」で終わる話だが、様々な面でこれは問題のある表現である。


一番簡単な問題は「溢れる」ということだ。コマンド行の固定長で上限ある。
このサイズは ARG_MAX という定数で定義されている。

例えば、Mac OS X 10.5.8 では以下の感じだ。
grep -R ARG_MAX /usr/include/
(中略)
/usr/include/limits.h:#define _POSIX_ARG_MAX 4096
/usr/include/sys/syslimits.h:#define ARG_MAX   (256 * 1024) /* max bytes for an exec function */
POSIX な定数だとわずか 4KB, そうでなくても 256KB 程度になる。これ以上は保証されない。

上記の find の書き方だと「カレントディレクトリ (.) とそこから下のサブディレクトリ」を再帰するので、debug.txt と書かれたファイルが下のディレクトリにあるなら容易に溢れる訳だ。溢れると、のたうち回った末にこんなエラーが表示される

/usr/bin/grep: Argument list too long.
計算機資源に優しくないのでやめろ感じる訳だ。

また、サブディレクトリを探す意図はなかったというかも知れないが、ならばシェルの補完でも十分だ。「 grep ERROR debug*.txt 」で間に合う。どうしてもバッククオートを使いたいなら「grep ERROR `ls debug*.txt`」とでもすればいい。


もちろん「溢れ」の可能性については前世紀から分かってる事実であり、このため find には頼もしい助っ人である「xargs」コマンドがついている。

上記の例を「悪い」見本で直すとこんな感じだ
find . -name "debug*.txt" -print | xargs grep ERROR
xargs は標準入力で渡されたリストを ARG_MAX を越えない程度に分割して、それを引数で渡されたコマンドの引数にして実行する。また、コマンドの実行が一々増えていいのなら

find . -name "debug*.txt" -exec grep ERROR {}\;
と書けばいい。-exec は find がマッチしたファイルパスの一つ一つに対して指定のコマンドを実行する。{} の部分がファイルパスに置換される。また、-exec の引数となるコマンド列の末尾は ; で終わるが、これがシェルに解釈されないように \ を入れる。-exec hogehoge {} \; まではイディオムであり、よく見かけると思われる。

個人的には、上記二つで普通のUNIX屋であると認識する。


で、より上位の人に「一々 -exec するな」か「マッチしたファイル名に空白がはいってたらどうする」かで突っ込まれるのもまた普通の話と思われる。

そう、xargs で引き渡された標準入力に、普通の英数字以外が混じっているとそこが「区切り」と見なされることがある。


で、だ。そんなことも、xargs を作ったような在りし日のUNIX屋にとっては想定内である。そのため、find には -print0, xargs には -0 オプションがある。正しいのは以下になるだろう。
find . -name "debug*.txt" -print0 | xargs -0 grep ERROR
find の -print0 オプションは -print とほぼ同じだが、区切り文字を '\0'、ヌル文字にしてくれる。空白や制御文字はファイル名に入れることは出来ても、ヌル文字は決してファイル名に現れない。このため、一つの検出されたファイルパスが間違って分割されることもない。

一方、ヌル文字が区切りであることを示すのに、xargs には -0 (ゼロ)オプションがある。find の -print0 での出力を xargs -0 は正しく解釈し、ARG_MAXに越えないだけにまとめてコマンド実行をしてくれる。

いつ頃から find -print0 と xargs -0 があったかは知らないが、私が駆け出しの頃にはすでにあったものだ。今日日の環境ならどこにでもあるだろう。

ファイル名に空白があるは、かつて Apple も iTunes のアップデートでミスった問題だ。
決して少ない訳じゃない。


決して新しい知識ではないのだから、ここまでは配慮して欲しいと思ったのが、本日ぐだぐだ言ってた理由だ。








2012/04/24

vExpert 2012

今年も辛うじて、vExpert 2012 を頂きました。ありがとうございます。