【问题标题】:Taming shlex.split() behaviour驯服 shlex.split() 行为
【发布时间】:2021-12-22 19:53:12
【问题描述】:

还有其他关于 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)

但随后program.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,我仍然会被第二个参数中包含的双引号所困扰,这是不应该的。

【问题讨论】:

    标签: python shlex


    【解决方案1】:

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

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

    shlex 类使编写词法分析器变得容易类似于 Unix shell 的简单语法。

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

    警告 shlex 模块仅适用于 Unix shell。

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

    【讨论】:

    • 你指出来并没有错,但它并没有真正帮助回答这个问题。需要指出的另一件事是,有人尝试为 Windows 编写 shlex,特别是 winshlex,但遗憾的是,在 UNC 路径方面,这种特殊的努力存在同样的问题(尽管它解决了其他一些问题)跨度>
    • @Grismar 你是对的——这可能应该是一个评论,但结果太长了!
    【解决方案2】:

    对于-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 表示。

    【讨论】:

    • 我同意将引号放在-o 选项之外,尽管在某些用例中也无法正常工作,这更多地与我正在处理的软件问题有关。然而,选择性地加倍反斜杠将是一个新问题。关键是我需要能够处理出现在批处理脚本中的命令,所以我无法控制诸如反斜杠加倍和不加倍之类的东西 - 我只想要一个不会弄乱的拆分斜杠/反斜杠的数量,但似乎shlex.split 不会是它。
    • 为什么不使用asyncio.create_subprocess_shell()来避免这个问题?
    • @blamy 对于大多数面临这个问题的人来说,这可能是获胜的建议。就我而言,我正在调用使用create_subprocess_exec 的其他人的代码,所以我没有选择 - 但这绝对是最好的建议。事实上,我可能只是复制了他们的代码部分来覆盖他们的代码,因为它或多或少是独立的,而且它可能比尝试“修复”shlex.split 更少黑客攻击(尽管工作量更大)。但是,我想要做的一部分是过滤掉一些参数并替换特定的其他参数 - 如果没有 shlex.split,这项工作会变得更加困难。
    【解决方案3】:

    现在,我最终得到了这个(可以说是有点 hack):

    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)
    

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

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

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

    我会暂时保留这个问题,以防有人想指出为什么上面的想法真的很糟糕,或者如果有人有更好的想法。

    【讨论】:

      猜你喜欢
      • 2019-11-05
      • 1970-01-01
      • 1970-01-01
      • 2014-01-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多