shlex ——简单词汇分析

源代码: Lib/shlex.py


这个 shlex 类使为类似于UnixShell的简单语法编写词汇分析器变得容易。这对于编写小型语言(例如,在Python应用程序的运行控制文件中)或分析带引号的字符串通常很有用。

这个 shlex 模块定义以下功能:

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

拆分字符串 s 使用类似shell的语法。如果 评论False (默认),将禁用解析给定字符串中的注释(设置 commenters 的属性 shlex 空字符串的实例)。默认情况下,此函数在POSIX模式下运行,但如果 位置 参数为假。

注解

自从 split() 函数实例化 shlex 实例,传递 None 对于 s 将从标准输入中读取要拆分的字符串。

3.9 版后已移除: 经过 None 对于 s 将在将来的Python版本中引发异常。

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 新版功能.

shlex.quote(s)

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

警告

这个 shlex 模块是 仅为Unix shell设计

这个 quote() 函数在不符合POSIX的外壳或来自其他操作系统(如Windows)的外壳上不保证正确。在这类shell上执行此模块引用的命令可能会造成命令插入漏洞。

考虑使用将命令参数与列表一起传递的函数,例如 subprocess.run() 使用 shell=False

这个成语不安全:

>>> 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 新版功能.

这个 shlex 模块定义以下类:

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

A shlex 实例或子类实例是词汇分析器对象。初始化参数(如果存在)指定从何处读取字符。它必须是一个类似文件/流的对象, read()readline() 方法或字符串。如果没有给出参数,输入将从 sys.stdin . 第二个可选参数是文件名字符串,它设置 infile 属性。如果 流内 参数被省略或等于 sys.stdin ,第二个参数默认为“stdin”。这个 位置 参数定义操作模式:何时 位置 不是真的(默认值),则 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 文件夹。

SLEX对象

A shlex 实例具有以下方法:

shlex.get_token()

返回令牌。如果已使用 push_token() 从堆栈中弹出一个标记。否则,从输入流中读取一个。如果读取遇到文件的直接结尾, eof 返回(空字符串 ('' )在非POSIX模式下,以及 None 在POSIX模式下。

shlex.push_token(str)

将参数推送到令牌堆栈上。

shlex.read_token()

读取原始令牌。忽略回推堆栈,不解释源请求。(这通常不是一个有用的入口点,在这里记录只是为了完整性。)

shlex.sourcehook(filename)

什么时候? shlex 检测源请求(请参见 source 下面)将此方法作为参数提供以下标记,并期望返回一个由文件名和一个打开的类似文件的对象组成的元组。

通常,此方法首先从参数中去掉任何引号。如果结果是绝对路径名,或者没有生效的先前源请求,或者先前的源是流(例如 sys.stdin ,结果将单独显示。否则,如果结果是相对路径名,则源包含堆栈上文件名前面的目录部分将被预先处理(此行为类似于C预处理器处理的方式 #include "file.h"

操作的结果将被视为文件名,并作为元组的第一个组件返回,其中 open() 调用它以生成第二个组件。(注意:这与实例初始化中的参数顺序相反!)

这个钩子是公开的,因此您可以使用它来实现目录搜索路径、添加文件扩展名和其他名称空间黑客。没有对应的“close”挂钩,但shlex实例将调用 close() 源输入流返回EOF时的方法。

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

shlex.push_source(newstream, newfile=None)

将输入源流推送到输入堆栈上。如果指定了文件名参数,它将在以后的错误消息中可用。这与 sourcehook() 方法。

shlex.pop_source()

从输入堆栈中弹出最后一个推送的输入源。这与lexer在堆栈输入流上达到eof时在内部使用的方法相同。

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

此方法以unix c编译器错误标签的格式生成错误消息前导;格式为 '"%s", line %d: ' 那里 %s 替换为当前源文件的名称和 %d 使用当前输入行号(可选参数可用于重写这些参数)。

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

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

shlex.commenters

被认为是注释初学者的字符串。从评论初学者到行尾的所有字符都被忽略。包括公正 '#' 默认情况下。

shlex.wordchars

将累积为多字符标记的字符串。默认情况下,包括所有ASCII字母数字和下划线。在POSIX模式中,还包括拉丁-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

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

shlex.instream

输入流,从中 shlex 实例正在读取字符。

shlex.source

这个属性是 None 默认情况下。如果为其分配一个字符串,则该字符串将被识别为类似于 source 各种外壳中的关键字。也就是说,紧接着的令牌将作为文件名打开,输入将从该流中获取,直到EOF,此时 close() 将调用该流的方法,输入源将再次成为原始输入流。源请求可以层叠任意深度。

shlex.debug

如果此属性是数字并且 1 或更多 shlex 实例将打印其行为的详细进度输出。如果您需要使用它,您可以阅读模块源代码来了解详细信息。

shlex.lineno

源行号(到目前为止看到的新行数加上一行)。

shlex.token

令牌缓冲区。在捕获异常时检查这一点可能很有用。

shlex.eof

用于确定文件结尾的标记。这将被设置为空字符串 ('' ,在非POSIX模式下,以及 None 在POSIX模式下。

shlex.punctuation_chars

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

3.6 新版功能.

解析规则

在非POSIX模式下操作时, shlex 将努力遵守以下规则。

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

  • 无法识别转义字符;

  • 引号中的封闭字符保留引号中所有字符的文字值;

  • 右引号分隔词 ("Do"Separate 被解析为 "Do"Separate

  • 如果 whitespace_splitFalse ,任何未声明为单词字符、空格或引号的字符都将作为单个字符标记返回。如果是 Trueshlex 只在空白处拆分单词;

  • 用空字符串表示EOF (''

  • 即使引用了,也不可能解析空字符串。

在POSIX模式下操作时, shlex 将尝试遵守以下解析规则。

  • 引号被删去,不分开单词 ("Do"Not"Separate" 被解析为单个单词 DoNotSeparate

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

  • 引号中不属于 escapedquotes (例如) "'" )保留引号内所有字符的文字值;

  • 引号中包含的字符是 escapedquotes (例如) '"' )保留引号中所有字符的文字值,但中提到的字符除外。 escape . 转义符只有在使用中的引号或转义符本身之后才保留其特殊含义。否则转义符将被视为普通字符。

  • EOF用一个 None 价值;

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

改进了与外壳的兼容性

3.6 新版功能.

这个 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 作为标点符号参数的值,可以传递带有特定字符的字符串,该字符串将用于确定构成标点符号的字符。例如::

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

但是,为了尽可能接近外壳,建议始终使用 posixwhitespace_split 使用时 punctuation_chars ,这将否定 wordchars 完全。

为了达到最佳效果, punctuation_chars 应与 posix=True . (注意 posix=False 是的默认值 shlex