24.3. shlex — 単純な字句解析

ソースコード: Lib/shlex.py


shlex クラスは Unix シェルに似た、単純な構文に対する字句解析器を簡単に書けるようにします。このクラスはしばしば、 Python アプリケーションのための実行制御ファイルのような小規模言語を書く上で便利です。

shlex モジュールは以下の関数を定義しています:

shlex.split(s, comments=False, posix=True)

シェルに似た文法を使って、文字列 s を分割します。 commentsFalse (デフォルト値) の場合、受理した文字列内のコメントを解析しません (shlex インスタンスの commenters メンバの値を空文字列にします)。この関数はデフォルトでは POSIX モードで動作し、 posix 引数が偽の場合は非 POSIX モードで動作します。

注釈

split() 関数は shlex クラスのインスタンスを利用するので、 sNone を渡すと標準入力から分割する文字列を読み込みます。

shlex.quote(s)

文字列 s をシェルエスケープして返します。戻り値は、リストを使えないようなケースで、シェルコマンドライン内で一つのトークンとして安全に利用出来る文字列です。

以下のイディオムは安全ではないかもしれません:

>>> filename = 'somefile; rm -rf ~'
>>> command = 'ls -l {}'.format(filename)
>>> print(command)  # executed by a shell: boom!
ls -l somefile; rm -rf ~

quote() がそのセキュリティホールをふさぎます:

>>> command = 'ls -l {}'.format(quote(filename))
>>> print(command)
ls -l 'somefile; rm -rf ~'
>>> remote_command = 'ssh home {}'.format(quote(command))
>>> print(remote_command)
ssh home 'ls -l '"'"'somefile; rm -rf ~'"'"''

クォーティングは UNIX シェルならびに split() と互換です:

>>> remote_command = split(remote_command)
>>> remote_command
['ssh', 'home', "ls -l 'somefile; rm -rf ~'"]
>>> command = split(remote_command[-1])
>>> command
['ls', '-l', 'somefile; rm -rf ~']

バージョン 3.3 で追加.

shlex モジュールは以下のクラスを定義します:

class shlex.shlex(instream=None, infile=None, posix=False)

shlex クラスとサブクラスのインスタンスは、字句解析器オブジェクトです。初期化引数を与えると、どこから文字を読み込むかを指定できます。指定先は read() メソッドと readline() メソッドを持つファイル/ストリーム類似オブジェクトか、文字列でなくてはいけません。引数が与えられなければ、 sys.stdin から入力を受け付けます。第 2 引数は、ファイル名を表す文字列で、 infile メンバの値の初期値を決定します。 instream 引数が省略された場合や、この値が sys.stdin である場合、第2引数のデフォルト値は “stdin” になります。 posix 引数は動作モードを定義します: posix が真でない場合(デフォルト)、 shlex インスタンスは互換モードで動作します。 POSIX モードで動作中、 shlex は、できる限り POSIX シェルの解析規則に似せようとします。

参考

configparser モジュール

Windows .ini ファイルに似た設定ファイルのパーザ。

24.3.1. shlex オブジェクト

shlex インスタンスは以下のメソッドを持っています:

shlex.get_token()

トークンを一つ返します。トークンが push_token() で使ってスタックに積まれていた場合、トークンをスタックからポップします。そうでない場合、トークンを一つ入力ストリームから読み出します。読み出し即時にファイル終了子に遭遇した場合、 eof (非 POSIX モードでは空文字列 ('')、POSIX モードでは None) が返されます。

shlex.push_token(str)

トークンスタックに引数文字列をスタックします。

shlex.read_token()

生 (raw) のトークンを読み出します。プッシュバックスタックを無視し、かつソースリクエストを解釈しません。 (通常これは便利なエントリポイントではありません。完全性のためにここで記述されています。)

shlex.sourcehook(filename)

shlex がソースリクエスト (下の source を参照してください) を検出した際、このメソッドはその後に続くトークンを引数として渡され、ファイル名と開かれたファイル類似オブジェクトからなるタプルを返すとされています。

通常、このメソッドはまず引数から何らかのクオートを取り除きます。処理後の引数が絶対パス名であった場合か、以前に有効になったソースリクエストが存在しない場合か、以前のソースが (sys.stdin のような) ストリームであった場合、この結果はそのままにされます。そうでない場合で、処理後の引数が相対パス名の場合、ソースインクルードスタックにある直前のファイル名からディレクトリ部分が取り出され、相対パスの前の部分に追加されます (この動作は C 言語プリプロセッサにおける #include "file.h" の扱いと同様です)。

これらの操作の結果はファイル名として扱われ、タプルの最初の要素として返されます。同時にこのファイル名で open() を呼び出した結果が二つ目の要素になります (注意: インスタンス初期化のときとは引数の並びが逆になっています!)

このフックはディレクトリサーチパスや、ファイル拡張子の追加、その他の名前空間に関するハックを実装できるようにするために公開されています。 ‘close’ フックに対応するものはありませんが、shlex インスタンスはソースリクエストされている入力ストリームが EOF を返した時には close() を呼び出します。

ソーススタックをより明示的に操作するには、 push_source() および pop_source() メソッドを使ってください。

shlex.push_source(newstream, newfile=None)

入力ソースストリームを入力スタックにプッシュします。ファイル名引数が指定された場合、以後のエラーメッセージ中で利用することができます。 sourcehook() メソッドが内部で使用しているのと同じメソッドです。

shlex.pop_source()

最後にプッシュされた入力ソースを入力スタックからポップします。字句解析器がスタック上の入力ストリームの EOF に到達した際に利用するメソッドと同じです。

shlex.error_leader(infile=None, lineno=None)

このメソッドはエラーメッセージの論述部分を Unix C コンパイラエラーラベルの形式で生成します; この書式は '"%s", line %d: ' で、%s は現在のソースファイル名で置き換えられ、%d は現在の入力行番号で置き換えられます (オプションの引数を使ってこれらを上書きすることもできます)。

このやり方は、 shlex のユーザに対して、Emacs やその他の Unix ツール群が解釈できる一般的な書式でのメッセージを生成することを推奨するために提供されています。

shlex サブクラスのインスタンスは、字句解析を制御したり、デバッグに使えるような public なインスタンス変数を持っています:

shlex.commenters

コメントの開始として認識される文字列です。コメントの開始から行末までのすべてのキャラクタ文字は無視されます。標準では単に '#' が入っています。

shlex.wordchars

複数文字からなるトークンを構成するためにバッファに蓄積していくような文字からなる文字列です。標準では、全ての ASCII 英数字およびアンダースコアが入っています。

shlex.whitespace

空白と見なされ、読み飛ばされる文字群です。空白はトークンの境界を作ります。標準では、スペース、タブ、改行 (linefeed) および復帰 (carriage-return) が入っています。

shlex.escape

エスケープ文字と見なされる文字群です。これは POSIX モードでのみ使われ、デフォルトでは '\' だけが入っています。

shlex.quotes

文字列引用符と見なされる文字群です。トークンを構成する際、同じクオートが再び出現するまで文字をバッファに蓄積します (すなわち、異なるクオート形式はシェル中で互いに保護し合う関係にあります)。標準では、ASCII 単引用符および二重引用符が入っています。

shlex.escapedquotes

quotes のうち、 escape で定義されたエスケープ文字を解釈する文字群です。これは POSIX モードでのみ使われ、デフォルトでは '"' だけが入っています。

shlex.whitespace_split

この値が True であれば、トークンは空白文字でのみで分割されます。たとえば shlex がシェル引数と同じ方法で、コマンドラインを解析するのに便利です。

shlex.infile

現在の入力ファイル名です。クラスのインスタンス化時に初期設定されるか、その後のソースリクエストでスタックされます。エラーメッセージを構成する際にこの値を調べると便利なことがあります。

shlex.instream

shlex インスタンスが文字を読み出している入力ストリームです。

shlex.source

このメンバ変数は標準で None を取ります。この値に文字列を代入すると、その文字列は多くのシェルにおける source キーワードに似た、字句解析レベルでのインクルード要求として認識されます。すなわち、その直後に現れるトークンをファイル名として新たなストリームを開き、そのストリームを入力として、EOF に到達するまで読み込まれます。新たなストリームの EOF に到達した時点で close() が呼び出され、入力は元の入力ストリームに戻されます。ソースリクエストは任意のレベルの深さまでスタックしてかまいません。

shlex.debug

このメンバ変数が数値で、かつ 1 またはそれ以上の値の場合、 shlex インスタンスは動作に関する冗長な進捗報告を出力します。この出力を使いたいなら、モジュールのソースコードを読めば詳細を学ぶことができます。

shlex.lineno

ソース行番号 (遭遇した改行の数に 1 を加えたもの) です。

shlex.token

トークンバッファです。例外を捕捉した際にこの値を調べると便利なことがあります。

shlex.eof

ファイルの終端を決定するのに使われるトークンです。非 POSIX モードでは空文字列 ('')、POSIX モードでは None が入ります。

24.3.2. 解析規則

非 POSIX モードで動作中の shlex は以下の規則に従おうとします。

  • ワード内の引用符を認識しない (Do"Not"Separate は単一ワード Do"Not"Separate として解析されます);

  • エスケープ文字を認識しない;

  • 引用符で囲まれた文字列は、引用符内の全ての文字リテラルを保持する;

  • 閉じ引用符でワードを区切る ("Do"Separate は、"Do"Separate であると解析されます);

  • whitespace_splitFalse の場合、wordchar、 whitespace または quote として宣言されていない全ての文字を、単一の文字トークンとして返す。 True の場合、 shlex は空白文字でのみ単語を区切る。

  • 空文字列 ('') で EOF を送出する;

  • 引用符に囲んであっても、空文字列を解析しない。

POSIX モードで動作中の shlex は以下の解析規則に従おうとします。

  • 引用符を取り除き、引用符で単語を分解しない ("Do"Not"Separate" は単一ワード DoNotSeparate として解析されます);

  • 引用符で囲まれないエスケープ文字群 ('\' など) は直後に続く文字のリテラル値を保持する;

  • escapedquotes でない引用符文字 ("'" など) で囲まれている全ての文字のリテラル値を保持する

  • 引用符に囲まれた escapedquotes に含まれる文字 ('"' など) は、 escape に含まれる文字を除き、全ての文字のリテラル値を保持する。エスケープ文字群は使用中の引用符、または、そのエスケープ文字自身が直後にある場合のみ、特殊な機能を保持する。他の場合にはエスケープ文字は普通の文字とみなされる。

  • None で EOF を送出する;

  • 引用符に囲まれた空文字列 ('') を許す。