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()¶
返回一个 token。如果已经使用
push_token()
堆叠了 token,则从堆栈中弹出一个 token。否则,从输入流中读取一个。如果读取时遇到立即文件结束,则返回eof
(在非 POSIX 模式下为空字符串 (''
),在 POSIX 模式下为None
)。
- shlex.push_token(str)¶
将参数压入 token 堆栈。
- shlex.read_token()¶
读取一个原始 token。忽略回推堆栈,并且不解释源请求。(这通常不是一个有用的入口点,这里记录它只是为了完整性。)
- shlex.sourcehook(filename)¶
当
shlex
检测到源请求时(参见下面的source
),此方法会接收到以下 token 作为参数,并期望返回一个由文件名和一个打开的文件类对象组成的元组。通常,此方法首先会去除参数上的任何引号。如果结果是绝对路径名,或者之前没有生效的源请求,或者之前的源是流(例如
sys.stdin
),则结果保持不变。否则,如果结果是相对路径名,则会在源包含堆栈中它之前的文件的名称的目录部分前面加上(此行为类似于 C 预处理器处理#include "file.h"
的方式)。操作的结果被视为文件名,并作为元组的第一个组件返回,并调用
open()
以生成第二个组件。(请注意:这与实例初始化中的参数顺序相反!)公开此钩子是为了方便您使用它来实现目录搜索路径、添加文件扩展名和其他命名空间 hacks。没有相应的“关闭”钩子,但是当 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
子类的实例有一些公共实例变量,这些变量要么控制词法分析,要么可以用于调试。
- shlex.commenters¶
被识别为注释起始符的字符字符串。从注释起始符到行尾的所有字符都将被忽略。默认情况下仅包含
'#'
。
- shlex.wordchars¶
将累积到多字符 token 中的字符字符串。默认情况下,包括所有 ASCII 字母数字字符和下划线。在 POSIX 模式下,还包括 Latin-1 集中带有重音符号的字符。如果
punctuation_chars
不为空,则可能出现在文件名规范和命令行参数中的字符~-./*?=
也将包含在此属性中,并且punctuation_chars
中出现的任何字符都将从wordchars
中删除(如果它们存在于那里)。如果whitespace_split
设置为True
,这将不起作用。
- shlex.whitespace¶
将被视为空格并跳过的字符。空格界定 token。默认情况下,包括空格、制表符、换行符和回车符。
- shlex.escape¶
将被视为转义符的字符。这仅在 POSIX 模式下使用,默认情况下仅包含
'\'
。
- shlex.quotes¶
将被视为字符串引号的字符。token 会一直累积,直到再次遇到相同的引号(因此,不同的引号类型会像在 shell 中一样相互保护。)默认情况下,包括 ASCII 单引号和双引号。
- shlex.whitespace_split¶
如果为
True
,则 token 将仅在空格处分割。例如,这对于使用shlex
解析命令行,以类似于 shell 参数的方式获取 token 非常有用。当与punctuation_chars
结合使用时,除了这些字符之外,token 还将在空格处分割。在 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
的默认值。)