0

SO上还有其他问题接近回答我的问题,但我有一个非常具体的用例,我无法解决。考虑一下:

from asyncio import create_subprocess_exec, run


async def main():
    command = r'program.exe "C:\some folder" -o"\\server\share\some folder" "a \"quote\""'
    proc = await create_subprocess_exec(*command)
    await proc.wait()


run(main())

这会引起麻烦,因为program.exe使用以下参数调用:

['C:\\some folder', '-o\\server\\share\\some folder', 'a "quote"']

也就是说,双反斜杠不再存在,因为它被shlex.split()删除了。当然,我可以改为(如其他答案所示)这样做:

    proc = await create_subprocess_exec(*command, posix=False)

但是 thenprogram.exe被这些参数有效地调用:

['"C:\\some folder"', '-o"\\\\server\\share\\some folder"', '"a \\"', 'quote\\""']

这也不好,因为现在双引号已经成为第一个参数内容的一部分,它们不属于它们,即使第二个参数现在很好。第三个参数已经变得一团糟。

用正斜杠替换反斜杠,或用正则表达式删除引号都不起作用,原因类似。

有没有办法shlex.split()在服务器名称之前留下双反斜杠?还是根本没有?为什么它首先删除它们?

请注意,它们本身是完全有效的命令(无论如何分别在 Windows 和 Linux 上):

program.exe "C:\some folder" -o"\\server\share\some folder"
echo "hello \"world""

即使我确实检测到了操作系统并posix=True/False相应地使用,我仍然会被第二个参数中包含的双引号所困扰,这是不应该的。

4

3 回答 3

0

对于 -o 参数,但其开头的前导 " 不在中间,并且反斜杠加倍

然后使用 posix=True

import shlex

command = r'program.exe "C:\some folder" -o"\\server\share\some folder" "a \"quote\""'

print( "Original command Posix=True", shlex.split(command, posix=True) )

command = r'program.exe "C:\some folder" "-o\\\\server\\share\\some folder" "a \"quote\""'

print( "Updated command Posix=True", shlex.split(command, posix=True) )

结果:

Original command Posix=True ['program.exe', 'C:\\some folder', '-o\\server\\share\\some folder', 'a "quote"']
Updated command Posix=True ['program.exe', 'C:\\some folder', '-o\\\\server\\share\\some folder', 'a "quote"']

结果中的反斜杠仍然是双倍的,但这是字符串中 \ 的标准 Python 表示。

于 2021-11-10T09:42:42.513 回答
0

据我所知,shlex如果您正在处理 Windows shell,则该模块是错误的工具。

文档的第一段说(我的斜体):

shlex 类可以很容易地为类似于 Unix shell 的简单语法编写词法分析器。

诚然,这仅涉及一个课程,而不是整个模块。后来,该quote函数的文档说(这次是原文中的粗体字):

警告 shlex 模块仅设计用于 Unix shell。

老实说,我不确定非 Posix 模式应该与什么兼容。可能是这样,但这只是我的猜测,shlex 的原始版本解析了它自己的语法,它与其他任何东西都不完全兼容,然后添加了 Posix 模式以实际上与 Posix shell 兼容。这个邮件列表线程,包括来自 ESR 的这个邮件似乎支持这一点。

于 2021-11-10T08:31:52.183 回答
0

现在,我最终得到了这个(可以说是一个黑客):

from os import name as os_name
from shlex import split


def arg_split(args, platform=os_name):
    """
    Like calling shlex.split, but sets `posix=` according to platform 
    and unquotes previously quoted arguments on Windows
    :param args: a command line string consisting of a command with arguments, 
                 e.g. r'dir "C:\Program Files"'  
    :param platform: a value like os.name would return, e.g. 'nt'
    :return: a list of arguments like shlex.split(args) would have returned
    """
    return [a[1:-1].replace('""', '"') if a[0] == a[-1] == '"' else a
            for a in (split(args, posix=False) if platform == 'nt' else split(args))]

使用它而不是shlex.split()让我得到我需要的东西,而不是破坏 UNC 路径。但是,我确信在某些极端情况下,不能正确处理双引号的正确转义,但它适用于我的所有测试用例,并且似乎适用于迄今为止的所有实际用例。使用风险自负。

@balmy 做了一个很好的观察,大多数人可能应该只使用:

command = r'program.exe "C:\some folder" -o"\\server\share\some folder" "a \"quote\""'
proc = await create_subprocess_shell(command)

代替

command = r'program.exe "C:\some folder" -o"\\server\share\some folder" "a \"quote\""'
proc = await create_subprocess_exec(*command)

但是,请注意,这意味着:

应用程序有责任确保正确引用所有空格和特殊字符以避免 shell 注入漏洞。shlex.quote() 函数可用于正确转义字符串中的空格和特殊 shell 字符,这些字符将用于构造 shell 命令。

这仍然是一个问题,因为quote()Windows 也不能正常工作(按设计)。

如果有人想指出为什么以上是一个非常糟糕的主意,或者如果有人有更好的主意,我会暂时保留这个问题。

于 2021-11-10T23:01:38.323 回答