# 24.3. `shlex` - 简单词法分析

源代码： Lib / shlex.py

`shlex`类可以轻松地为类似于Unix shell的简单语法编写词法分析器。这通常用于编写minilanguages（例如，在Python应用程序的运行控制文件中）或用于解析引用的字符串。

`shlex`模块定义以下函数：

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

  使用类shell语法拆分字符串s。If comments is `False` (the default), the parsing of comments in the given string will be disabled (setting the `commenters` attribute of the `shlex` instance to the empty string). 默认情况下，此函数在POSIX模式下运行，但如果posix参数为假，则使用非POSIX模式。

  注意

  由于`split()`函数实例化一个`shlex`实例，传递`None`用于s标准输入。

`shlex.``quote`(s)

  返回字符串s的shell转义版本。返回的值是一个字符串，可以安全地用作shell命令行中的一个令牌，对于不能使用列表的情况。

  这个成语是不安全的：

      >>> 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 shell兼容，并且`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)

  A `shlex`实例或子类实例是词法分析器对象。初始化参数（如果存在）指定从哪里读取字符。它必须是具有`read()`和`readline()`方法的文件/流类对象或字符串。如果没有给出参数，则将从`sys.stdin`获取输入。第二个可选参数是文件名字符串，用于设置`infile`属性的初始值。如果省略instream参数或等于`sys.stdin`，则此第二个参数默认为“stdin”。posix参数定义操作模式：当posix不为true（默认）时，`shlex`实例将以兼容模式运行。当在POSIX模式下操作时，`shlex`将尝试尽可能接近POSIX外壳解析规则。

也可以看看

模块`configparser`
  解析器，用于类似于Windows `.ini`文件的配置文件。

## 24.3.1. shlex对象

`shlex`实例具有以下方法：

`shlex.``get_token`()

  返回标记。如果使用`push_token()`堆叠令牌，请从堆栈中弹出令牌。否则，从输入流中读取一个。如果读取遇到立即文件结束，则返回`eof`（非POSIX模式下的空字符串（`''`）和`None`

`shlex.``push_token`(str)

  将参数推送到令牌堆栈。

`shlex.``read_token`()

  阅读原始令牌。忽略后推堆栈，并且不解释源请求。（这通常不是一个有用的切入点，并且在这里仅仅是为了完整性的记录。）

`shlex.``sourcehook`(filename)

  当`shlex`检测到源请求时（参见下面的`source`），此方法将给出以下令牌作为参数，并且希望返回由文件名和打开的文件组成的元组，像对象。

  通常，此方法首先剥离参数的任何引号。如果结果是绝对路径名，或者没有生效的上一个源请求，或者上一个源是流（例如`sys.stdin`），则结果将保留。否则，如果结果是相对路径名，则在源包含堆栈上紧邻其前的文件的名称的目录部分被前置（该行为类似于C预处理器处理`#include > t1> “file.h”`）。

  操作的结果被视为文件名，并作为元组的第一个组件返回，`open()`调用它产生第二个组件。（注意：这是与实例初始化中的参数顺序相反的！）

  这个钩子是暴露的，所以你可以使用它来实现目录搜索路径，添加文件扩展名和其他命名空间黑客。没有相应的“关闭”钩子，但是当它返回EOF时，shlex实例将调用源输入流的`close()`方法。

  要更明确地控制源堆叠，请使用`push_source()`和`pop_source()`方法。

`shlex.``push_source`(newstream, newfile=None)

  将输入源流推送到输入堆栈。如果指定了filename参数，它以后将可用于错误消息。这是由`sourcehook()`方法内部使用的相同方法。

`shlex.``pop_source`()

  从输入堆栈弹出最后推入的输入源。这是当词法分析器在堆叠输入流上到达EOF时内部使用的相同方法。

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

  此方法以Unix C编译器错误标签的格式生成错误消息引导程序；格式为`'“％s”， 行 ％d： ' 其中`%s`替换为当前源文件的名称，`%d`替换为当前输入行号（可选参数可用于覆盖这些）。`

  提供此便利是为了鼓励`shlex`用户以Emacs和其他Unix工具理解的标准可解析格式生成错误消息。

`shlex`子类的实例具有一些公共实例变量，它们控制词法分析或可用于调试：

`shlex.``commenters`

  被识别为注释初学者的字符串。将忽略评论初学者到行尾的所有字符。默认情况下只包含`'#'`。

`shlex.``wordchars`

  将会累积到多字符令牌中的字符串。默认情况下，包括所有ASCII字母数字和下划线。

`shlex.``whitespace`

  将被视为空格并被跳过的字符。空白限制令牌。默认情况下，包括空格，制表符，换行符和回车。

`shlex.``escape`

  将被视为逃逸的字符。这将仅在POSIX模式下使用，默认情况下只包括`'\'`。

`shlex.``quotes`

  将被视为字符串引号的字符。令牌累积，直到再次遇到相同的引号（因此，不同的引号类型在shell中保护对方）。默认情况下，包括ASCII单引号和双引号。

`shlex.``escapedquotes`

  `quotes`中的字符，用于解释`escape`中定义的转义字符。这只在POSIX模式下使用，默认情况下只包括`'"'`。

`shlex.``whitespace_split`

  如果`True`，则标记将仅分割为空格。这很有用，例如，用`shlex`解析命令行，以类似于shell参数的方式获取令牌。

`shlex.``infile`

  当前输入文件的名称，最初在类实例化时设置或由以后的源请求堆叠。在构造错误消息时检查这可能是有用的。

`shlex.``instream`

  此`shlex`实例的输入流正在读取字符。

`shlex.``source`

  默认情况下，此属性为`None`。如果为其分配一个字符串，该字符串将被识别为词汇级包含请求，类似于各种shell中的`source`关键字。也就是说，紧随其后的令牌将作为文件名打开，并且将从该流中获取输入，直到EOF，此时将调用该流的`close()`方法，并且输入源将再次成为原始输入流。源请求可以被堆叠在任何数量级的深度。

`shlex.``debug`

  如果此属性为数字且`1`或更多，则`shlex`实例将在其行为上打印详细的进度输出。如果你需要使用这个，你可以阅读模块的源代码来了解细节。

`shlex.``lineno`

  源行号（到目前为止看到的换行符的计数加一）。

`shlex.``token`

  令牌缓冲区。在捕获异常时检查这可能是有用的。

`shlex.``eof`

  令牌用于确定文件结束。在POSIX模式下，这将设置为空字符串（`''`），在非POSIX模式下设置为`None`

## 24.3.2. Parsing Rules

当在非POSIX模式下操作时，`shlex`将尝试遵守以下规则。

- 不能在字中识别引号字符（`Do"Not"Separate`被解析为单个字`Do"Not"Separate`）；
- 无法识别转义字符；
- 在引号中包含字符保留引号内所有字符的字面值值；
- 结束引号分隔单词（`"Do"Separate`被解析为`"Do"`和`Separate`）；
- 如果`whitespace_split`为`False`，则未声明为字字符，空格或引号的任何字符将作为单字符令牌返回。如果`True`，`shlex`将只分割空格中的单词；
- EOF用空字符串（`''`）用信号通知；
- 它不可能解析空字符串，即使引用。

当在POSIX模式下操作时，`shlex`将尝试服从以下解析规则。

- 引号被剥离，不要分隔单词（`"Do"Not"Separate"`被解析为单个单词`DoNotSeparate`）；
- 非引号转义字符（例如，`'\'`）保留下一个字符的字面值值；
- 将不是`escapedquotes`（例如`"'"`）一部分的引号中的字符保留引号内所有字符的字面值值；
- Enclosing characters in quotes which are part of `escapedquotes` (e.g. `'"'`) preserves the literal value of all characters within the quotes, with the exception of the characters mentioned in `escape`. 转义字符只有在使用中引用引号或转义字符本身时才保留其特殊含义。否则，转义字符将被视为正常字符。
- EOF用`None`值用信号通知；
- 允许引用空字符串（`''`）。