shlex —简单的词法分析

源代码: Lib/shlex.py


shlex类使您可以轻松地为类似于 Unix Shell 的简单语法编写词法分析器。这对于编写迷你语言(例如,在 Python 应用程序的运行控制文件中)或解析带引号的字符串通常很有用。

shlex模块定义以下Function:

  • shlex. split(* s comments = False posix = True *)
    • 使用类似 shell 的语法分割字符串* s 。如果 comments False(默认值),则将禁用对给定字符串中的 Comments 的解析(将shlex实例的commenters属性设置为空字符串)。默认情况下,此函数在 POSIX 模式下运行,但如果 posix *参数为 false,则使用非 POSIX 模式。

Note

由于split()函数实例化shlex实例,因此将None传递给* s *将会读取要从标准 Importing 中拆分的字符串。

  • shlex. join(* split_command *)
    • 连接列表* split_command *的标记并返回一个字符串。此函数是split()的反函数。
>>> from shlex import join
>>> print(join(['echo', '-n', 'Multiple words']))
echo -n 'Multiple words'

返回的值将转义为 shell 换行,以防止注入漏洞(请参阅quote())。

3.8 版的新Function。

  • shlex. quote(* s *)
    • 返回字符串* s *的脱壳版本。返回的值是一个字符串,在不能使用列表的情况下,可以安全地用作 shell 命令行中的一个标记。

这个习惯用法是不安全的:

>>> 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 shell 和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 ~']

版本 3.3 中的新Function。

shlex模块定义以下类:

    • class * shlex. shlex(* instream = None infile = None posix = False punctuation_chars = False *)
    • shlex实例或子类实例是词法分析器对象。初始化参数(如果存在)指定从何处读取字符。它必须是具有read()readline()方法的类似于文件/流的对象,或者是字符串。如果没有给出参数,则 Importing 将取自sys.stdin。第二个可选参数是文件名字符串,它设置infile属性的初始值。如果* instream *参数Ellipsis或等于sys.stdin,则第二个参数默认为“ stdin”。 * posix 参数定义操作模式:如果 posix *不为 true(默认值),则shlex实例将在兼容模式下操作。在 POSIX 模式下运行时,shlex将try尽可能接近 POSIX Shell 解析规则。 * punctuation_chars 参数提供了一种使行为更接近于实际 shell 解析方式的方法。这可以采用多个值:默认值False保留了在 Python 3.5 及更早版本中看到的行为。如果设置为True,则将更改字符();<>|&的解析:这些字符的任何运行(考虑为标点符号)均作为单个标记返回。如果设置为非空字符串,则这些字符将用作标点字符。 wordchars属性中出现在 punctuation_chars *中的所有字符将从wordchars中删除。有关更多信息,请参见改善了与 Shell 的兼容性。 * punctuation_chars *只能在创建shlex实例时设置,以后不能修改。

在版本 3.6 中更改:添加了* punctuation_chars *参数。

See also

  • Module configparser

  • 解析器,用于类似于 Windows .ini文件的配置文件。

shlex Objects

shlex实例具有以下方法:

  • shlex. get_token ( )

    • 返回令牌。如果令牌已使用push_token()进行堆叠,请从堆叠中弹出令牌。否则,请从 Importing 流中读取一个。如果读取遇到文件的立即结束,则返回eof(在非 POSIX 模式下为空字符串('',在 POSIX 模式下为None)。
  • shlex. push_token(* str *)

    • 将参数压入令牌堆栈。
  • shlex. read_token ( )

    • 读取原始令牌。忽略推回堆栈,并且不解释源请求。 (这通常不是有用的切入点,此处仅出于完整性目的在此处进行了说明.)
  • shlex. sourcehook(* filename *)

    • shlex检测到源请求时(请参见下面的source),该方法将获得以下标记作为参数,并期望返回一个由文件名和类似文件的打开对象组成的 Tuples。

通常,此方法首先会删除参数中的所有引号。如果结果是绝对路径名,或者没有有效的先前的源请求,或者先前的源是流(例如sys.stdin),则结果将保留。否则,如果结果是相对路径名,则在源包含堆栈上紧接在文件名之前的文件名的目录部分会被加上前缀(此行为类似于 C 预处理器处理#include "file.h"的方式)。

操纵的结果被视为文件名,并返回为 Tuples 的第一个组件,并在其上调用open()以产生第二个组件。 (注意:这与实例初始化中的参数 Sequences 相反!)

该钩子是公开的,因此您可以使用它来实现目录搜索路径,添加文件 extensions 和其他名称空间黑客。没有相应的“关闭”钩子,但是 shlex 实例在返回 EOF 时将调用源 Importing 流的close()方法。

若要更明确地控制源堆栈,请使用push_source()pop_source()方法。

  • shlex. push_source(* newstream newfile = None *)

    • 将 Importing 源流推入 Importing 堆栈。如果指定了 filename 参数,则以后可在错误消息中使用它。这与sourcehook()方法内部使用的方法相同。
  • shlex. pop_source ( )

    • 从 Importing 堆栈中弹出最后按下的 Importing 源。当词法分析器在堆叠的 Importing 流上达到 EOF 时,这是内部使用的相同方法。
  • shlex. error_leader(* infile = None lineno = None *)

    • 此方法以 Unix C 编译器错误标签的格式生成错误消息引导符;格式为'"%s", line %d: ',其中%s替换为当前源文件的名称,%d替换为当前 Importing 的行号(可使用可选参数覆盖它们)。

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

shlex子类的实例具有一些公共实例变量,这些变量可以控制词法分析或可用于调试:

  • shlex. commenters

    • 被视为 CommentsStarters 的字符串。从 CommentsStarters 到行尾的所有字符都将被忽略。默认情况下仅包含'#'
  • shlex. wordchars

    • 将累积为多字符标记的字符串。默认情况下,包括所有 ASCII 字母数字和下划线。在 POSIX 模式下,还包括 Latin-1 集中的重音字符。如果punctuation_chars不为空,则出现在文件名规范和命令行参数中的字符~-./*?=也将包含在此属性中,并且如果出现在punctuation_chars中的任何字符都将从wordchars中删除。如果whitespace_split设置为True,则将无效。
  • shlex. whitespace

    • 将被视为空格并被跳过的字符。空格限制标记。默认情况下,包括空格,制表符,换行符和回车符。
  • shlex. escape

    • 将被视为转义符的字符。仅在 POSIX 模式下使用,默认情况下仅包含'\'
  • shlex. quotes

    • 将被视为字符串引号的字符。令牌会累积,直到再次遇到相同的引号为止(因此,不同的引号类型会像在 shell 中一样相互保护.)默认情况下,包括 ASCII 单引号和双引号。
  • shlex. escapedquotes

    • quotes中的字符将解释escape中定义的转义字符。仅在 POSIX 模式下使用,默认情况下仅包含'"'
  • shlex. whitespace_split

    • 如果为True,则令牌只会在空格中分割。例如,这对于使用shlex解析命令行,以类似于 shell 参数的方式获取令牌很有用。与punctuation_chars结合使用时,除这些字符外,令牌还将在空白处分割。

在版本 3.8 中更改:使punctuation_chars属性与whitespace_split属性兼容。

  • shlex. infile

    • 当前 Importing 文件的名称,最初是在类实例化时设置的,或者由以后的源请求堆叠在一起的。在构造错误消息时检查这一点可能很有用。
  • shlex. instream

    • shlex实例正在从中读取字符的 Importing 流。
  • shlex. source

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

    • 如果此属性为数字且1或更大,则shlex实例将在其行为上输出详细的进度输出。如果需要使用它,可以阅读模块源代码以了解详细信息。
  • shlex. lineno

    • 源行号(到目前为止已看到的换行数加一)。
  • shlex. token

    • 令牌缓冲区。在捕获异常时对此进行检查可能很有用。
  • shlex. eof

    • 用于确定文件结尾的令牌。在非 POSIX 模式下,它将被设置为空字符串(''),在 POSIX 模式下将被设置为None
  • shlex. punctuation_chars

    • 只读属性。将被视为标点符号的字符。标点符号字符将作为单个令牌返回。但是,请注意,将不会执行语义有效性检查:例如,即使 shell 可能无法识别“ >>>”作为令牌,也可以将其返回。

3.6 版的新Function。

Parsing Rules

在非 POSIX 模式下运行时,shlex将try遵守以下规则。

  • 单词内不能识别引号字符(Do"Not"Separate被解析为单个单词Do"Not"Separate);

  • 无法识别转义字符;

  • 用引号引起来的字符保留引号内所有字符的字面值;

  • 用引号引起来的单词分开("Do"Separate被解析为"Do"Separate);

  • 如果whitespace_splitFalse,则任何未语句为单词字符,空格或引号的字符都将作为单字符标记返回。如果是Trueshlex只会在空白处分割单词;

  • EOF 用空字符串('')发出 signal;

  • 即使引用,也无法解析空字符串。

在 POSIX 模式下运行时,shlex将try遵守以下解析规则。

  • 引号被删除,并且不要分隔单词("Do"Not"Separate"被解析为单个单词DoNotSeparate);

  • 不带引号的转义字符(例如'\')保留后面的下一个字符的 Literals 值;

  • 将不包含在escapedquotes(例如"'")中的引号引起来的字符保留引号内所有字符的 Literals 值;

  • 用引号括起escapedquotes(例如'"')的字符可以保留引号内所有字符的 Literals 值,但escape中提到的字符除外。只有在使用引号或转义字符本身时,转义字符才保留其特殊含义。否则,转义字符将被视为普通字符。

  • EOF 以None值表示;

  • 允许带引号的空字符串('')。

改进了与 Shell 的兼容性

3.6 版的新Function。

shlex类提供了与通用 Unix shell(例如bashdashsh)执行的解析的兼容性。要利用这种兼容性,请在构造函数中指定punctuation_chars参数。默认为False,保留 3.6 之前的行为。但是,如果将其设置为True,则将更改字符();<>|&的解析:这些字符的任何运行都将作为单个令牌返回。尽管缺少完整的 Shell 解析器(考虑到 Shell 的多样性,该解析器在标准库的范围之外),但它确实使您比其他方式更容易执行命令行处理。为了说明这一点,您可以在以下代码片段中看到不同之处:

>>> 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', ')']

当然,将返回对于 shell 无效的令牌,并且您将需要对返回的令牌实施自己的错误检查。

可以传递带有特定字符的字符串,而不是传递True作为 punctuation_chars 参数的值,该字符串将用于确定构成标点的字符。例如:

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

Note

指定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?']

但是,为了尽可能紧密地匹配 Shell,建议在使用punctuation_chars时始终使用posixwhitespace_split,这将完全否定wordchars

为了获得最佳效果,应将_与posix=True一起设置。 (请注意posix=Falseshlex的默认值。)