マクロツイーター

はてダから移行した記事の表示が崩れてますが、そのうちに直せればいいのに(えっ)

TeX コマンドの引数を理解したい話

コマンドシェルにおいて TeX エンジンのコマンド(tex とか uplatex とか suyahtex とか)を起動するときは、通常は、入力の TeX ソースファイル名*1を引数に入れる。当然である。

uplatex j62sicp

ところが、TeX エンジンのコマンド行の書式は実際にはもっと複雑である。このことは以前に説明したことがある。*2簡単のため、オプション指定(-xxx)とフォーマット指定(&xxx)の件を省略すると、以前の記事では次のように説明されている。

  • 原則として、“最初の引数”は入力ファイル名であり、それより後の引数列は TeX コードと解釈される。例えば、次のようなコマンド行の場合:*3
    [コマンドプロンプト]
    tex foo \ShowQuux 42 54 ; \bye
    [bashシェル]
    tex foo '\ShowQuux' 42 54 ';' '\bye'
    まず foo.tex が読み込まれてそこに書かれたコードが実行された後、(まだ終了していないなら)「\ShowQuux 42 54 ; \bye」というコードが実行される。
  • しかし、“最初の引数”が「\」で始まる場合は、それを含む全ての引数列が TeX コードと解釈される。例えば、次のようなコマンド行の場合:
    [コマンドプロンプト]
    tex \show \ShowQuux 42 54 ; \bye
    [bashシェル]
    tex '\show' '\ShowQuux' 42 54 ';' '\bye'
    ファイル読込は行われず、「\show \ShowQuux 42 54 ; \bye」というコードが実行される。*4

この説明の根拠となっているのは、「tex --help」を実行した時に表示されるヘルプメッセージである。

Usage: tex [OPTION]... [TEXNAME[.tex]] [COMMANDS]
   or: tex [OPTION]... \FIRST-LINE
   or: tex [OPTION]... &FMT ARGS
  Run TeX on TEXNAME, usually creating TEXNAME.dvi.
  Any remaining COMMANDS are processed as TeX input, after TEXNAME is read.
  If the first line of TEXNAME is %&FMT, and FMT is an existing .fmt file,
  use it.  Else use `NAME.fmt', where NAME is the program invocation name,
  most commonly `tex'.

  Alternatively, if the first non-option argument begins with a backslash,
  interpret all non-option arguments as a line of TeX input.

  Alternatively, if the first non-option argument begins with a &, the
  next word is taken as the FMT to read, overriding all else.  Any
  remaining arguments are processed as above.

  If no arguments or options are specified, prompt for input.
何かおかしい挙動

普通はこの理解で構わない。ところが、変態なことをし始めると、これでは説明のつかない場合が出てくる。

「%foo.tex」という名前の TeX ソースファイルを作って、それをコンパイルしようとして次のコマンドを実行したとする。((Windowsコマンドプロンプトでは「%XXX%」で環境変数の値を表すという規則があるが、少なくとも単独の「%」はそのまま「%」と解釈される。))

tex %foo

ところが、何故か %foo.tex は読み込まれず((読み込まれたのなら端末に「(./%foo.tex)」のメッセージが出るはずである。))、そのまま止まってしまう。

This is TeX, Version 3.14159265 (TeX Live 2015/W32TeX) (preloaded format=tex)
 encTeX v. Jun. 2004, reencoding enabled.

*

ここで Enter を空打ちすると、その直後に「ファイルが見つからない」というエラーが発生する。しかも探索されたのは「./」であり、要するに空文字列の名前のファイル名である。

! I can't find file `./'.
<to be read again>
                   \par
<*>

(Press Enter to retry, or Control-Z to exit)
Please type another input file name: ^Z

%」は TeX特殊文字(コメント開始文字)ではあるが、しかし「\」ではないので、“最初の引数”はファイル名となり、従って“TeX コードの文法”は無関係なはずである。

もしかしたら、「\」でなくても、先頭文字が“TeX特殊文字”の場合は、ファイル名扱いにならない(つまり TeX コード扱いになる)のではないか。これなら、「%foo」が“どこかに消えてしまった”のは頷ける。ただ、その説明に従うと、動作は引数無しで「tex」だけで起動した場合と同じになるはずである。((この場合、まず先頭行プロンプト「**」が表示される。そこで Enter を空打ちすると、「Please type the name of your input file.」というメッセージが出る。))ところが上記の挙動はそれとも一致しない。

そもそも、先頭の文字が特殊文字でない場合でも、“先頭の引数”が単純にファイル名になるとは限らない。

tex foo%bar

だと、まず「*」プロンプトが出て、そこで Enter を空打ちすると、(「foo%bar」ではなく)「foo」が見つからないというエラーになる。さらに、

[コマンドプロンプト]
tex foo\the\year
[bashシェル]
tex 'foo\the\year'

だと、先と同様の挙動で、今度は「foo2015」が見つからないというエラーになる。どうやら、「“先頭の引数”のファイル名の文字列も“TeX コードのように”扱われている」のは確かである。しかし何故一度「*」のプロンプトが出るのだろう?

うっ……、至極当たり前なことを言われた……。

TeXbook してみた

TeXbook で“コマンド行の引数”について解説されているのは以下の部分。

Incidentally, many systems allow you to invoke TeX by typing a one-liner like ‘tex story’ instead of waiting for the ‘**’; similarly, ‘tex \relax’ works for Experiment 1, and ‘tex &plain story’ loads the plain format before inputting the story file.
The TeXbook, Chapter 6: Running TeX

要するに、実行コマンドの引数に何かを入れるのは、(引数無しで起動して出てくる)先頭行プロンプト「**」に同じ文字列を入れるのと等価である、といっている。この辺りの話は例の記事でも行ったのだが、TeXbook では飽くまでもコマンド行引数は便利な代替であり、先頭行プロンプトの方が“本来の”方法という立場をとっている。((コマンド行を使う方法はシステム依存が起こりうる。実際、これまでの例でみたように、“コマンドシェルの特殊文字”のせいで、「実際に TeX に渡したいのは \show だが bash では '\show'\\show と書く必要がある、のような依存性が起こっている。TeX 実行中のプロンプトを扱うのであれば、このような依存性を気にする必要がなくなるのである。))そして、その先頭行プロンプトについては以下のような説明がある。

You might wonder why the first prompt was ‘**’, while the subsequent ones are ‘*’; the reason is simply that the first thing you type to TeX is slightly different from the rest: If the first character of your response to ‘**’ is not a backslash, TeX automatically inserts ‘\input’.
(同上)

おお……実に単純な話であった。

引数の先頭文字が「\」以外なら前に \input を補う

という規則なのである。つまり“先頭の引数”だけファイル名として別扱いする、なんて規則はなくて、引数の文字列は常に TeX コードとして扱われるわけなのである。

例えば、一番普通の「uplatex j62sicp」のようなコマンド行の場合、先頭文字(j)は「\」でないので、次のようなソース行が読み込まれたのと等価になる。

\input j62sicp

従って、その直後に「j62sicp.tex」が読み込まれることになる。

次に、「tex \show \ShowQuux 42 54 ; \bye」のコマンド行の場合は、先頭が「\」なので、\input が挿入されずに次のソース行が読み込まれる。

\show \ShowQuux 42 54 ; \bye

次にいよいよ変態な場合を考えよう。コマンド行が「tex %foo」の場合は、先頭が「\」でないので、次のようになる。

\input %foo

%」以降はコメントだから、これでは \input の引数がまだ終結していないことになる(以前の記事で述べたように、\input の引数を終結するためには空白文字トークンか非文字トークンが必要)。従って、TeX は「**5プロンプトを出したのである。ここで Enter を空打ちすると、これまで入力されたソースは以下のようになる。

\input %foo

2 行目は空行だから、結局上のソースは「\input\par」と等価で、\par\input の引数を終結させるので、引数は空文字列となる。従って、この時点で空文字列の名前のファイルを探してエラーになったわけである。

もう一つ、「tex foo\the\year」の例を見てみよう。起動直後の時点の“読み込まれるソース”は以下のようになる。

\input foo\the\year

1 行目の行末は制御綴があるので、ここには \input の引数を終結させる空白文字トークンが存在しない。その後は先の例と同様である。

練習問題

コマンドプロンプト*6で以下のコマンドを実行した直後(Enter 空打ち等はしない時点)で何か入力ファイルが探索されるか。その場合、ファイル名は何であるか。

tex \the\year.tex\relax \vfill
tex foo\empty bar\empty baz\relax quux\relax
tex foo\the\year gee_whiz

*1:拡張子を省略した場合は .tex が補われる。

*2:この記事は pTeX-ng という新しいエンジンと従来のエンジンのコマンド行書式の比較を行ったものであるが、今回の話題は従来のエンジンの方である。

*3:以降、tex コマンドを例に用いるが、pdftex や uplatex などの他の TeXLaTeX も含む)のコマンドでも話は全く同じである。

*4:ファイルが全く読み込まれなかったので、jobname は“texput”となる。

*5:もはや先頭行ではない。

*6:あるいは〝シェルの特殊文字”が問題の範囲内では無関係なコマンドシェル。