"shlex" --- 単純な字句解析
**************************

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

======================================================================

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

"shlex" モジュールは以下の関数を定義します。

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

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

   バージョン 3.12 で変更: *s* 引数に "None" を渡すと、"sys.stdin" か
   ら読み込む代わりに例外を送出するようになりました。

shlex.join(split_command)

   リスト *split_command* のトークンを連結して文字列を返します。 この
   関数は "split()" の逆の操作を行います。

   >>> from shlex import join
   >>> print(join(['echo', '-n', 'Multiple words']))
   echo -n 'Multiple words'

   戻り値には、インジェクション脆弱性を防ぐためにシェルのエスケープ処
   理を施します ("quote()" を参照)。

   Added in version 3.8.

shlex.quote(s)

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

   警告:

     "shlex" モジュールは **Unix シェル専用に設計されています**。
     "quote()" 関数は POSIX に準拠していないシェルや Windows など他の
     OS のシェルでは正しく動作する保証がありません。 そのようなシェル
     でこのモジュールによってクォートされたコマンドを実行すると、コマ
     ンドインジェクションの脆弱性を生むおそれがあります。"shell=False"
     を指定した "subprocess.run()" など、コマンドの引数をリストで渡す
     関数の利用を検討してください。

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

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

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

   >>> from shlex import 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()" と互換です:

   >>> from shlex import 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 ~']

   Added in version 3.3.

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

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

   "shlex" またはそのサブクラスのインスタンスは字句解析器オブジェクト
   です。 初期化時の最初の引数を指定すると、文字の読み込み元を設定でき
   ます。 この引数は "read()" メソッドと "readline()" メソッドを持つフ
   ァイルやストリームに似たオブジェクト、または文字列でなければなりま
   せん。 引数を省略すると "sys.stdin" から入力を読み込みます。 省略可
   能な二つ目の引数はファイル名の文字列で、"infile" 属性の初期値を設定
   します。 *instream* 引数を省略するか "sys.stdin" を指定した場合、二
   つ目の引数のデフォルトは "stdin" です。 *posix* 引数は動作モードを
   決めます。 *posix* が真でない場合 (デフォルト)、"shlex" インスタン
   スは互換モードで動作します。 POSIX モードでは "shlex" はできる限り
   POSIX シェルの解析規則に従おうとします。 *punctuation_chars* 引数を
   使うと、実際のシェルの解析動作にさらに近づけられます。 この引数には
   いくつかの値を指定できます。 デフォルトの "False" は Python 3.5 以
   前の動作を維持します。 "True" に設定すると文字 "();<>|&" の解析が変
   わり、これらの文字 (区切り記号と見なされます) の連続を一つのトーク
   ンとして返します。 空でない文字列に設定すると、その文字列の各文字を
   区切り記号として使います。 "wordchars" 属性内の文字のうち
   *punctuation_chars* に含まれるものは "wordchars" から削除されます。
   詳細は シェルとの互換性の向上 を参照してください。
   *punctuation_chars* は "shlex" インスタンスの作成時にのみ設定でき、
   後から変更できません。

   バージョン 3.6 で変更: *punctuation_chars* 引数が追加されました。

参考:

  "configparser" モジュール
     Windows ".ini" ファイルに似た設定ファイルのパーザ。


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 のすべての英数字とアンダースコアを含みます。 POSIX モードでは
   Latin-1 のアクセント付き文字も含みます。 "punctuation_chars" が空で
   ない場合、ファイル名の指定やコマンドライン引数に現れる文字
   "~-./*?=" もこの属性に追加され、"punctuation_chars" に含まれる文字
   は "wordchars" から削除されます。 "whitespace_split" が "True" の場
   合、この属性は効果を持ちません。

shlex.whitespace

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

shlex.escape

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

shlex.quotes

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

shlex.escapedquotes

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

shlex.whitespace_split

   "True" の場合、空白だけでトークンを分割します。 例えば "shlex" でコ
   マンドラインを解析し、シェルの引数と同じようにトークンを取得する際
   に便利です。 "punctuation_chars" と組み合わせた場合は、そこに指定し
   た文字に加えて空白でもトークンを分割します。

   バージョン 3.8 で変更: "punctuation_chars" 属性を
   "whitespace_split" 属性と併用できるようになりました。

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" が入ります。

shlex.punctuation_chars

   読み取り専用のプロパティです。 区切り記号と見なす文字を表します。
   区切り記号の連続は一つのトークンとして返されます。 ただし意味的に妥
   当かどうかの検査は行いません。 例えば、シェルがそのようなトークンと
   して認識しない場合でも '>>>' をトークンとして返すことがあります。

   Added in version 3.6.


解析規則
========

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

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

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

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

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

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

* 空文字列 ("''") で EOF を送出する

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

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

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

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

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

* "escapedquotes" に含まれる引用符 (例えば "'"'") で文字を囲むと、
  "escape" に指定された文字を除き、引用符内のすべての文字のリテラル値
  が保持されます。 エスケープ文字が特別な意味を持つのは、使用中の引用
  符またはエスケープ文字そのものが直後に続く場合だけです。 それ以外の
  場合、エスケープ文字は通常の文字として扱われます。

* "None" で EOF を送出する

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


シェルとの互換性の向上
======================

Added in version 3.6.

"shlex" クラスは "bash"、"dash"、"sh" など一般的な Unix シェルの解析と
の互換性を提供します。 この互換性を利用するには、コンストラクターで
"punctuation_chars" 引数を指定します。 デフォルトは "False" で、3.6 よ
り前の動作を維持します。 "True" に設定すると文字 "();<>|&" の解析が変
わり、これらの文字の連続を一つのトークンとして返します。 完全なシェル
のパーサーではありませんが (シェルは多種多様なので、完全な対応は標準ラ
イブラリの範囲を超えます)、この機能によってコマンドラインの処理をより
簡単に行えます。 次の例で動作の違いを確認できます。

   >>> import shlex
   >>> text = "a && b; c && d || e; f >'abc'; (def \"ghi\")"
   >>> s = shlex.shlex(text, posix=True)
   >>> s.whitespace_split = True
   >>> list(s)
   ['a', '&&', 'b;', 'c', '&&', 'd', '||', 'e;', 'f', '>abc;', '(def', 'ghi)']
   >>> s = shlex.shlex(text, posix=True, punctuation_chars=True)
   >>> s.whitespace_split = True
   >>> list(s)
   ['a', '&&', 'b', ';', 'c', '&&', 'd', '||', 'e', ';', 'f', '>', 'abc', ';',
   '(', 'def', 'ghi', ')']

もちろん、シェルでは無効なトークンも返されるため、返されたトークンのエ
ラーチェックは自分で実装する必要があります。

"punctuation_chars" 引数には "True" の代わりに特定の文字を並べた文字列
を渡すこともできます。 その文字列に含まれる文字が区切り記号になります
。 例えば次のように使います。

   >>> import shlex
   >>> s = shlex.shlex("a && b || c", punctuation_chars="|")
   >>> list(s)
   ['a', '&', '&', 'b', '||', 'c']

注釈:

  "punctuation_chars" を指定すると、"wordchars" 属性に文字 "~-./*?="
  が追加されます。 これらの文字はファイル名 (ワイルドカードを含みます)
  やコマンドライン引数 (例えば "--color=auto") に現れるためです。 その
  ため次のようになります。

     >>> import shlex
     >>> s = shlex.shlex('~/a && b-c --color=auto || d *.py?',
     ...                 punctuation_chars=True)
     >>> list(s)
     ['~/a', '&&', 'b-c', '--color=auto', '||', 'd', '*.py?']

  ただし、シェルの動作にできるだけ近づけるには、"punctuation_chars" を
  使うときは常に "posix" と "whitespace_split" を併用することをお勧め
  します。 その場合、"wordchars" はまったく効果を持ちません。

最良の結果を得るには、"punctuation_chars" と一緒に "posix=True" も設定
してください。 ("shlex" のデフォルトは "posix=False" であることに注意
してください。)
