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
的預設值。)