shlex
— 简单词法分析¶
源代码: Lib/shlex.py
shlex
类使得为类似于 Unix shell 的简单语法编写词法分析器变得容易。这通常对于编写小型语言(例如,用于 Python 应用程序的运行控制文件)或解析带引号的字符串非常有用。
shlex
模块定义了以下函数
- shlex.split(s, comments=False, posix=True)¶
使用类似 shell 的语法拆分字符串 s。如果 comments 为
False
(默认值),则将禁用对给定字符串中注释的解析(将commenters
实例的shlex
属性设置为字符串)。此函数默认情况下以 POSIX 模式运行,但如果 posix 参数为 false,则使用非 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'
返回值经过 shell 转义,以防止注入漏洞(请参阅
quote()
)。3.8 版新加入.
- shlex.quote(s)¶
返回字符串 s 的 shell 转义版本。返回值是一个字符串,可以在 shell 命令行中安全地用作一个标记,适用于不能使用列表的情况。
警告
shlex
模块仅设计用于 Unix shell。quote()
函数不能保证在非 POSIX 兼容的 shell 或来自其他操作系统(如 Windows)的 shell 上正确执行。在此类 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)¶
shlex
实例或子类实例是一个词法分析器对象。初始化参数(如果存在)指定从哪里读取字符。它必须是一个具有read()
和readline()
方法的文件/流式对象,或者是一个字符串。如果没有给出参数,则将从sys.stdin
获取输入。第二个可选参数是一个文件名字符串,它设置infile
属性的初始值。如果省略 instream 参数或其等于sys.stdin
,则第二个参数默认为“stdin”。posix 参数定义了操作模式:当 posix 不为 true(默认值)时,shlex
实例将以兼容模式运行。当以 POSIX 模式运行时,shlex
将尝试尽可能接近 POSIX shell 解析规则。punctuation_chars 参数提供了一种使行为更接近于真实 shell 解析方式的方法。它可以采用多个值:默认值False
保留了 Python 3.5 及更早版本中的行为。如果设置为True
,则字符();<>|&
的解析将发生变化:这些字符(被视为标点字符)的任何连续出现都将作为单个标记返回。如果设置为一个非空的字符字符串,则这些字符将用作标点字符。出现在 punctuation_chars 中的wordchars
属性中的任何字符都将从wordchars
中删除。有关更多信息,请参阅 改进的 Shell 兼容性。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()¶
读取原始标记。忽略回推堆栈,并且不解释源请求。(这通常不是一个有用的入口点,这里记录它只是为了完整性。)
- shlex.sourcehook(filename)¶
当
shlex
检测到源请求时(请参阅下面的source
),此方法将接收下一个标记作为参数,并期望返回一个由文件名和打开的类文件对象组成的元组。通常,此方法首先会去除参数中的任何引号。如果结果是绝对路径名,或者之前没有生效的源请求,或者之前的源是流(例如
sys.stdin
),则结果将保持不变。否则,如果结果是相对路径名,则会将源包含堆栈中紧邻其之前的文件名的目录部分添加到其前面(此行为类似于 C 预处理器处理#include "file.h"
的方式)。操作结果将被视为文件名,并作为元组的第一个组件返回,并调用
open()
来生成第二个组件。(注意:这与实例初始化中参数的顺序相反!)之所以公开此钩子,是为了让您可以使用它来实现目录搜索路径、文件扩展名的添加以及其他命名空间技巧。没有相应的“关闭”钩子,但是当 shlex 实例在堆叠的输入流上到达 EOF 时,它将调用源输入流的
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", line %d: '
,其中%s
替换为当前源文件的名称,%d
替换为当前输入行号(可以使用可选参数来覆盖这些值)。提供此便利是为了鼓励
shlex
用户以 Emacs 和其他 Unix 工具可以理解的标准、可解析格式生成错误消息。
shlex
子类的实例具有一些公共实例变量,这些变量要么控制词法分析,要么可用于调试
- shlex.commenters¶
被识别为注释开头的字符串。从注释开头到行尾的所有字符都将被忽略。默认情况下仅包含
'#'
。
- shlex.wordchars¶
将累积成多字符标记的字符字符串。默认情况下,包括所有 ASCII 字母数字和下划线。在 POSIX 模式下,还包括 Latin-1 集中的重音字符。如果
punctuation_chars
不为空,则字符~-./*?=
(可以出现在文件名规范和命令行参数中)也将包含在此属性中,并且punctuation_chars
中出现的任何字符(如果存在于wordchars
中)都将从wordchars
中删除。如果whitespace_split
设置为True
,则这将不起作用。
- shlex.whitespace¶
将被视为空格并被跳过的字符。空格界定标记。默认情况下,包括空格、制表符、换行符和回车符。
- shlex.escape¶
将被视为转义符的字符。这仅在 POSIX 模式下使用,默认情况下仅包含
'\'
。
- shlex.quotes¶
将被视为字符串引号的字符。标记会一直累积,直到再次遇到相同的引号(因此,不同的引号类型会像在 shell 中一样相互保护)。默认情况下,包括 ASCII 单引号和双引号。
- shlex.whitespace_split¶
如果为
True
,则标记将仅在空格处拆分。例如,这对于使用shlex
解析命令行、以类似于 shell 参数的方式获取标记非常有用。与punctuation_chars
结合使用时,除了这些字符之外,标记还将在空格处拆分。3.8 版更改:
punctuation_chars
属性已与whitespace_split
属性兼容。
- shlex.infile¶
当前输入文件的名称,在类实例化时初始设置或由后续源请求堆叠。在构造错误消息时检查它可能很有用。
- shlex.source¶
此属性默认为
None
。如果为其分配一个字符串,则该字符串将被识别为类似于各种 shell 中的source
关键字的词法级别包含请求。也就是说,紧随其后的标记将作为文件名打开,并且输入将从该流中获取,直到 EOF,此时将调用该流的close()
方法,并且输入源将再次成为原始输入流。源请求可以堆叠任意数量的级别。
- shlex.lineno¶
源代码行号(到目前为止看到的换行符计数加一)。
- shlex.token¶
标记缓冲区。在捕获异常时检查它可能很有用。
- shlex.eof¶
用于确定文件结束的标记。在非 POSIX 模式下,这将设置为空字符串 (
''
),在 POSIX 模式下设置为None
。
- shlex.punctuation_chars¶
只读属性。将被视为标点的字符。标点字符的运行将作为单个标记返回。但是,请注意,不会执行语义有效性检查:例如,'>>>' 可以作为标记返回,即使 shell 可能无法识别它。
3.6 版新加入.
解析规则¶
在非 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
的引号(例如"'"
)括起来的字符保留引号内所有字符的字面值;用属于
escapedquotes
的引号(例如'"'
)括起来的字符保留引号内所有字符的字面值,但escape
中提到的字符除外。转义字符仅在使用引号或转义字符本身后才保留其特殊含义。否则,转义字符将被视为普通字符。EOF 用
None
值表示;允许使用带引号的空字符串 (
''
)。
改进与 Shell 的兼容性¶
3.6 版新加入.
shlex
类提供与常见 Unix shell(如 bash
、dash
和 sh
)执行的解析的兼容性。要利用此兼容性,请在构造函数中指定 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']
注意
当指定了 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
时始终使用 posix
和 whitespace_split
,这将完全否定 wordchars
。
为了获得最佳效果,应将 punctuation_chars
与 posix=True
一起设置。(请注意,posix=False
是 shlex
的默认值。)