spawn.exeは、MS-DOS/コマンドプロンプトウィンドウ(コンソールウィンドウ)を表示せずに、与えられたコマンド行の実行結果をテキストエディタ内に表示することを目的としたごく普通のコマンド行プログラムです。環境変数PATHで指定されているディレクトリに格納し、MS-DOS/コマンドプロンプトウィンドウでたとえば次のように入力すれば、
C:\>spawn dir
dirコマンドを実行し、その出力をレジストリで指定されたテキストエディタに送ることができます。GUIフロントエンドも、まったく同じようにしてspawnプログラムを起動しています(ただし、spawnに渡すコマンド行全体を「""」で囲んでいます)。
spawn.exeに、オプション以外に拡張子付きのデータファイル名を1つだけ与えると、その拡張子に対応付けられているプログラムが起動され、データファイルがそのプログラムのなかでオープンされます。指定されたファイルが存在しない場合には、まずそのファイルを作成します。この場合、/h、/v以外のオプションは、指定しても無視されます。以下のすべての説明は、この機能ではなく、先に説明した方の機能を扱ったものです。
Windowsはアプリケーションのためにパイプを制御するためのサービスを提供していますが、spawn.exeはそれを使っていません。OSがWindows 9x/Meならcommand.com、Windows NT/2000/XPならcmd.exeを起動し、これらシステム提供のコマンドプロセッサにコマンド行を解釈させています(Windows NT/2000/XPにもcommand.comというプログラムは存在し、おそらくcmd.exeから16ビットプログラムを起動したときなどに使われるのだろうと思われますが、両者はほぼ一体化しているように思われますので、ここではNT系システムのコマンド行シェルはcmd.exeという名前で呼ぶことにします)。たとえば、Windows 9x/Meで
C:>command.com /c dir
というコマンド行を実行すると、command.comが「dir」というコマンド行を解釈して実行します。MS-DOS/コマンドプロンプトウィンドウでは、単に
C:>dir
を実行するのと同じ結果になりますが、それはこれらのウィンドウでプロンプトを表示しているのがcommand.comなりcmd.exeだからです。プロンプトのないspawn.exeのなかでは、明示的にcommand.com(またはcmd.exe)を呼び出す必要があります。
自分でコマンド行を解釈せず、command.comやcmd.exeに解釈を任せ、それによりパイプの処理も任せる方法には、spawn.exeの作者がパイプ管理の面で楽をできるということ以外にもメリットがあります。それは、バッチファイルやシェルの内部コマンドも実行できるということです。バッチファイルというのは、いくつかのコマンド行をまとめたテキストファイルで、MS-DOSの時代からサポートされてきました。コマンド行でこのテキストファイルの名前を入力すると、ファイル内のコマンドを連続実行できます。command.comやcmd.exeは、このバッチファイルの内容を解釈して書かれているコマンドを1つずつ実行します。command.com、cmd.exeを使わずにspawn.exe自身でコマンド行を解釈する場合、バッチファイルの実行をサポートするつもりなら、コマンド名から通常の実行可能ファイルとバッチファイルを見分け、その内容を読み込んで解釈し、コマンドを実行する機能をspawn.exeに追加しなければなりませんが、command.com、cmd.exeとまったく同じ仕事をこなせるようにするには、かなりの時間と労力が必要です。また、dirなどのシェル内部コマンドは、command.comやcmd.exeが実装しています。コマンド行をこれらのシェルに渡さないのだとすれば、dirなどのシェルの内部コマンドもspawn.exeで実装しなければなりません(あるいは、実装しないで無視する)。spawn.exeでこれらの仕事をすべてこなすことによるメリットは、command.com、cmd.exeお任せ方式のメリットと比べてごく小さいと思います。
さて、最初に書いたように、spawn.exeは、コンソールウィンドウを表示せずにコマンド行プログラムを実行しようとしています。しかし、そのような環境で標準入力を読み出そうとするプログラムを入力ファイル引数なしで実行すると(たとえば、コマンド行パラメータをまったく指定せずにcatプログラムを実行すると)、プログラムは標準入力(キーボード入力)を永遠に待ち続けることになります。そのため、spawn.exeは、コマンド行に手を加え、空の標準入力を与えるようにしています。
少々専門的になりますが、空の標準入力を与えるための方法としてまず思いつくのは、NULデバイスのリダイレクトです。NULデバイスはMS-DOSの時代から存在する特殊ファイルです。標準入力をNULデバイスにリダイレクトすると空の入力が与えられ、標準出力をNULデバイスにリダイレクトすると出力をブラックホールに送り込むことができます。しかし、Windows 9x/Meでごく普通の32ビットフィルタプログラムを実行し、標準入力をNULデバイスにリダイレクトすると、プログラムはなぜかハングしてしまいます(16ビットプログラムならこのような問題は起きませんし、Windows NT/2000/XPでもこの問題は起きません)。MS-DOSプロンプトウィンドウでフィルタがハングしたら、[Ctrl-C]キーを押せばフィルタを強制終了できますが、spawn.exeから起動したときにはそういうわけにはいきません。そのため、NULデバイスは使えません。しかし、空の一時ファイルを作成し、標準入力をこのファイルにリダイレクトすれば、目的を達することができます。spawn.exeはこの方法を使っています。
一方、標準出力は、リダイレクトしなければ闇に捨てられてしまいますので、spawn.exeは空入力のために使っているのとはまた別の一時ファイルを作ってそこにリダイレクトしています。しかし、コマンド行のなかでリダイレクト先が指定されている場合には、一時ファイルではなく、そのリダイレクト先を使っています。コマンドが終了したら、レジストリで指定されているエディタにコマンド行パラメータとしてリダイレクト先のファイルを与えて実行します。ほとんどすべてのエディタは、コマンド行パラメータとしてファイル名を指定すると、そのファイルの内容を読み込んでウィンドウ内に表示します。このような仕組みになっているため、実行からエディタの起動までにタイムラグが発生しますが、特定のエディタだけではなく、任意のエディタを利用できる便利さを考えれば、少々の遅れは我慢できるはずです。
以上説明してきたように、コンソールウィンドウを使わずにテキストエディタに出力を流し込むためには、コマンド行を書き換えなければなりませんが、コマンド行の書き換えによって期待される動作と実際の動作にずれが生じないようにする必要があります。特に、command.comとcmd.exeには、名前が違うのと同じくらいの動作の違いがありますので、それぞれの場合について、コマンド行の書き換え方を変える必要があります。
command.comとcmd.exeの違いとは、どのようなことでしょうか。たとえば、cmd.exeは、2つのコマンドを続けて実行するための「&」、前のコマンドが成功したら後ろのコマンドを実行する「&&」、前のコマンドが失敗したら後ろのコマンドを実行する「||」、コマンドをグループ化する「()」という特殊文字をサポートしますが、command.comはサポートしません。また、command.comは/cオプションの後ろのコマンド行全体を「""」で囲むと、コマンド行を解釈できなくなってしまいますが、cmd.exeにはそのようなことはありません。さらに、cmd.exeは「2>1&」という表記によって標準エラー出力を標準出力にリダイレクトできますが、command.comではできません。その一方で、command.comを起動する親プロセスは、標準エラー出力のリダイレクト先を指定できますが、cmd.exeを起動する親プロセスで標準エラー出力のリダイレクト先を指定することはできませんし、NT 4では標準入出力についても親プロセスが指定した標準入出力が無視されるようです(私はまだこれについて書かれたドキュメントを見つけていませんので、確定的なことを言うことはできませんが)。最後に、cmd.exeはバッチファイル出力をリダイレクトできますが、command.comはうまくできないように見えます。そのため、行(cmdline)でバッチファイルを実行したときに、Windows 9x/Meでは空の出力しか得られません。
これらの違いを考慮し、起動するシェルがcommand.comの場合には、コマンド行を次のように書き換えています。
1.パイプ文字(|)が含まれていない場合、標準入力、標準出力ともリダイレクトされていなければ、コマンド行の末尾に「<intemp >outtemp」を追加します(intemp、outtempは、実際には#sp????.tmpというファイル名ですが──?の部分はシステムによって任意の16進数に置き換えられる──、この説明では全体を読みやすくするために仮にintemp、outtempというファイル名を使うことにします)。標準入力か標準出力の片方または両方がリダイレクトされているなら、intemp、outtempの代わりにそれらのファイル名を使います。
2.パイプ文字が含まれている場合には、最初のパイプ文字までの間に標準入力がリダイレクトされていなければ、最初のパイプ文字の前に「<intemp」を追加します。また、出力がリダイレクトされていなければ、コマンド行の末尾に「>outtemp」を追加します。
表にまとめると次のようになります。
変換前 |
変換後 |
cat |
cat <intemp > outtemp |
cat <some.txt |
cat <some.txt >outtemp |
cat <some.txt >test.txt |
cat <some.txt >test.txt |
cat | cat |
cat <intemp | cat >outtemp |
cat <some.txt | cat |
cat <some.txt | cat >outtemp |
cat some.txt | cat |
cat some.txt <intemp | cat >outtemp |
cat | cat < some.txt |
cat <intemp | cat < some.txt >outtemp |
cat | cat < some.txt > test.txt |
cat <intemp | cat < some.txt > test.txt |
cat > test.txt | cat |
cat > test.txt <intemp | cat (test.txtを表示) |
cat > test.txt | cat > test2.txt |
cat > test.txt <intemp | cat > test2.txt (test2.txtを表示。test.txtには書き込みは行われず、cat
<intemp | cat > test2.txtが実行される) |
なお、「cat | cat < some.txt」のようにすると、「|」よりも「<」の方が優先され、「cat |」からの出力は無視されますが、spawnが「cat | cat < some.txt >outtemp」というコマンド行をcommand.comに渡すと、最初のcatがキーボード入力を読もうとするため、このコマンド行は終了できなくなってしまいます。そのため、無駄なように見えるかもしれませんが、表のようなことをしています。また、「cat some.txt <intemp」のようにしても、ファイル名引数を与えられたcat(他のプログラムでも同様)は、標準入力から何も読もうとしないので、「cat some.txt」と同じ効果になります。
起動するシェルがcmd.exeの場合には、コマンド行を次のように書き換えます。
※与えられたコマンド行の前に「"」、後ろに「>outtemp 2>&1"」を付加します。また、最初の「|」までに標準入力がリダイレクトされていなければ、「|」の前に「<intemp」を付加します。さらに、&、&&、||で区切られた1つ1つのコマンドについて、標準入力がリダイレクトされていなければ、末尾に「<intemp」を付加します。標準出力がリダイレクトされているファイルがある場合には、それらのファイルの名前を記憶し、あとで全部のファイルをテキストエディタ内でオープンします。
表にまとめると次のようになります。
変換前 |
変換後 |
cat |
"(cat <intemp)>outtemp 2>&1" |
cat | cat |
"(cat <intemp|cat)>outtemp 2>&1" |
cat & cat |
"(cat <intemp & cat <intemp)>outtemp
2>&1" |
sort > test.txt & cat > test2.txt |
"(sort > test.txt <intemp & cat > test2.txt
<intemp)>outtemp 2>&1" (test.txt、test2.txt、outtempの3つのファイルが表示される) |
spawn.exeにはいくつかのオプションがあります。PCKの他のコマンド行プログラムでは、コマンド名よりも後ろであれば、任意の位置にオプションを指定できますが、spawn.exeの場合は、コマンド名の直後に指定しなければなりません。なお、オプションとしては大文字も使えます。
/c intempが空ファイルではなく、spawn起動時にクリップボードにセットされていたテキスト形式のデータを格納したファイルになります。クリップボードにテキスト形式のデータが格納されていない場合には、
Clipboard doesn't have text data.
□というエラーメッセージが表示されます。行(cmdline)以外のプログラムでは、「入力」グループボックスの「クリップボード」チェックボックス、行では「クリップボードの内容を標準入力として使う」チェックボックスをクリックすると、spawn起動時にこのオプションがセットされます。
/d command.comを使うときには標準出力のリダイレクト先の末尾、cmd.exeを使うときにはouttempの末尾に、
cat (in C:\)
From Tue, 14 Aug 2001 15:27:30 +0900(東京 (標準時))
To Tue, 14 Aug 2001 15:27:30 +0900(東京 (標準時))
119 bytes
□という形式で実行したコマンド行、カレントディレクトリ、実行開始時間、実行終了時間、上記の方法で書き換えたあとのコマンド行のバイト数が表示されます。GUIフロントエンドの「設定」ダイアログボックスで「出力の末尾にコマンド行とカレントディレクトリを付加」をチェックすると、spawn起動時にこのオプションがセットされます。
/l /dとよく似ていますが、出力の先頭に次のような形式でコマンド行を付加します。
C:\> cat
□GUIフロントエンドの「設定」ダイアログボックスで「出力の先頭にプロンプトとコマンド行を付加」をチェックすると、spawn起動時にこのオプションがセットされます。
/n command.comを使うときには標準出力のリダイレクト先は表示されなくなり、cmd.exeを使うときにはコマンド行が失敗しない限り、標準出力のリダイレクト先が表示されなくなります。cmd.exeで、「&」、「|」、「&&」、「||」などを使って複数のコマンドを実行すると、最後のコマンドの成否がコマンド行の成否になります。たとえば、「cat --uso | cat」のようなコマンド行を与えた場合、「|」の前の部分は失敗しますが、後ろの「cat」は成功するので、コマンド行全体としては成功ということになり、「cat --uso」が標準エラー出力に生成したはずのエラーメッセージは、標準出力とともに消えてしまいますので注意してください。一部のフロントエンドの「設定」ダイアログボックスに含まれている「標準出力のためにエディタを起動しない」をチェックすると、spawn起動時にこのオプションがセットされます。
/o=<File> <File>の部分はファイル名です。通常の出力のほか、指定されたファイルもテキストエディタでオープンします。複数回指定することができます。丁(tee)で指定された出力ファイルは、このオプションを使って表示されます。
/<NUM> <NUM>の部分は数字です。たとえば、/1とすると、レジストリのHKEY_CURRENT_USER\Software\Longtail\cmdline\setting\editorsキーの1という名前の値のテキストエディタを起動します。存在しない値を指定したり、このオプションを指定しなければ、エクスプローラで.txtという拡張子のファイルをダブルクリックしたときに起動されるプログラムが使われます。
/s 標準出力をクリップボードにもセットします。
/hまたは/? 簡単なヘルプメッセージが表示され、コマンド行は実行されません。
/v バージョン情報が表示され、コマンド行は実行されません。