python click 如何避免參數中的通配符被展開
在這篇討論中有被提到
Expand globs in arguments on Windows? · Issue #1096 · pallets/click (github.com)
click預設會通過 glob 展開通配符這是為了確保在 Windows 或 Linux 上能獲得一致的效果
不過反過來說如果你就是要傳入通配符,其實沒有任何手段可以避免的,原作似乎就沒開放了
討論的最後有提到改進在這裡
expand patterns in Windows args by davidism · Pull Request #1830 · pallets/click (github.com)
那就沒辦法只能硬幹了,就把這代碼幹掉
import click
# 定義一個新的 _expand_args 函數,直接返回原始參數
def _no_expand_args(args):
return args
# 猴子補丁覆蓋 Click 的 _expand_args 方法
from click import core
core._expand_args = _no_expand_args
@click.command()
@click.argument('path', type=str)
def process_path(path):
click.echo(f"Received path: {path}")
if __name__ == '__main__':
process_path()
這樣一來就不會被騷擾了,反過來說你也可以在這裡寫上屬於自己的條件
上面是簡寫完整一點的整個函式其實是長這樣的
# 定義一個新的 _expand_args 函數,直接返回原始參數
import typing as t
def _no_expand_args(
args: t.Iterable[str],
*,
user: bool = True,
env: bool = True,
glob_recursive: bool = True,
) -> t.List[str]:
return list(args)
# 猴子補丁覆蓋 Click 的 _expand_args 方法
from click import core
core._expand_args = _no_expand_args
原始代碼可以在這裡看,可以參考是如何用 glob 展開變數的
click/src/click/utils.py at 923d197b56caa9ffea21edeef5baf1816585b099 · pallets/click (github.com)
補充說明一下流程,我覺得 click 原作這個解不夠漂亮,但確實已經是沒有辦法中的一個還算可以的辦法了。
原作的做法是收到來自命令的參數時,就先進行展開,不管是 option 還是 augument 都會被展開,這個有個問題是
在 linux 上 bash 會直接展開後才輸入,所以實際 click 拿到的參數已經是展開後的,看起來很美好的統一了 Win 和 linux,其實背後是犧牲了一個特性
如果想試圖傳遞通配符給程序,通常的做法是在 bash 中輸入雙引號,以便告訴 bash 不要展開這個字串直接輸入到程序中
問題就在這裡,click拿到參數之後不管如何總之每個都先過一輪 glob() 就對了,所以最終還是會被展開了,儘管已經用雙引號
好處就是統一了 Windwos 跟 Linux 的邏輯,背後是犧牲透過雙引號輸入通配符號的可能性, Click 強制 glob() 導致無論如何參數都會被展開
不過其實有邏輯漏洞可以卡,透過這個方式 `myapp -p"*.py"` 或是全寫也行,這樣過 glob() 的時時候由於 `-p"*.py"` 不是路徑就不會被展開了
如果 -p 後面有空格,那就會被切開來分兩次進glob() 還是會被展開,類似技巧還有別的組合可以實現
繼續回來原本的話題,這個要比較合理的解決,估計還是只能開放 glob 的自由度吧,預設不要開,自己決定要不要展開,而且最好還是可以決定系統層級的 `glob_win=Ture`, `glob_unix=Ture`。
這樣至少 linux 上還可以保持透過雙引號控制要不要展開,而Windwos這沒辦法了終端機本來就不會展開了,不管雙不雙引號都一樣。
兩邊系統的統一性我覺得不是那個重要,對於 win 本來就要知道系統不會幫你展開,對於 unix 本來就要知道系統會擅自展開。
函式只需要提供一個快捷讓你自己決定要不要賭這個洞。
沒有留言:
張貼留言