Python模块用于shellquote/unshellquote?

37

Python标准库中是否有适用于解析/还原字符串以在shell命令中使用的工具?我正在寻找类似于Perl的String::ShellQuote::shell_quote的Python函数。

$ print String::ShellQuote::shell_quote("hello", "stack", "overflow's", "quite", "cool")
hello stack 'overflow'\''s' quite cool

而且更重要的是,需要一个能够实现相反操作(将字符串分解成列表)的方法。

8个回答

33

看起来像是

try:  # py3
    from shlex import quote
except ImportError:  # py2
    from pipes import quote

quote("hello stack overflow's quite cool")
>>> '"hello stack overflow\'s quite cool"'

这对我来说足够了。


1
当然,subprocess 对于在 Python 中实际启动进程非常好,但对于代码生成,这正是我所需要的!+1 - SingleNegationElimination
在Python 3中,这是shlex.quote - asmeurer
4
在Windows系统中,subprocess.list2cmdline更加精确。pipes.quote函数总是使用单引号,这在Windows命令行环境中是不可接受的。 - Rockallite
@asmeurer 但是在Python3中,管道符并没有被弃用。 - Ciasto piekarz
"Deprecated since version 2.7" : "自版本2.7废弃" "It is finally exposed publicly in Python 3.3 as the quote function in the shlex module." : "在Python 3.3中,它作为shlex模块中的quote函数最终公开。" - Denilson Sá Maia

11

8

如果要取消引用,请尝试使用shlex.split()


7

我相信pipes.quote已经失效了,不应该再使用它,因为它不能正确处理长度为零的参数:

>>> from pipes import quote
>>> args = ['arg1', '', 'arg3']
>>> print 'mycommand %s' % (' '.join(quote(arg) for arg in args))
mycommand arg1  arg3

我相信结果应该是这样的:
mycommand arg1 '' arg3

1
好吧,这还不够好。但是我们需要一个更好的解决方案 :-) - YGA
1
打印 'mycommand %s' % (' '.join(quote(arg) or "''" for arg in args)) - Day
4
在John的倡议下,这个问题在Python 2.6中得到了修复。 - Søren Løvborg
但是在这里使用join的作用大多数都被quote所取代了! - Sylvain Leroux
你为什么这么说? - John Wiseman

6

关于 shell 引用,这个方法适用:在 Posix 上进行了严格测试。[我假设 Python 提供的 list2cmdline 函数在 Windows 上像宣传的那样工作]

# shell.py
import os
if os.name == 'nt':
    from subprocess import list2cmdline

    def quote(arg):
        return list2cmdline([arg])[0]
else:
    import re
    _quote_pos = re.compile('(?=[^-0-9a-zA-Z_./\n])')

    def quote(arg):
        r"""
        >>> quote('\t')
        '\\\t'
        >>> quote('foo bar')
        'foo\\ bar'
        """
        # This is the logic emacs uses
        if arg:
            return _quote_pos.sub('\\\\', arg).replace('\n',"'\n'")
        else:
            return "''"

    def list2cmdline(args):
        return ' '.join([ quote(a) for a in args ])

如果有人关心,这些测试的链接在这里。


反例:待引用的字符串包含 "\xC3\xA9",它在 UTF-8 中表示 é,在文件名中是很常见的。上面的代码在这两个字符前面加上了反斜杠,这是不正确的。pipes.quote 会将其放入单引号中。 - greggo

2
quote函数已经有一段时间了(Python 2.7?)-- 其主要缺点是在3.2和3.3之间从pipe模块移动到了shlex
在导入该函数时,您必须准备处理两种情况。
try:
    from shlex import quote
except ImportError:
    from pipes import quote

2

标准库模块subprocess有list2cmdline函数可以做到这一点,虽然根据Microsoft的规则,我不确定它在类Unix环境中用于更复杂的命令行时是否可靠。


0

你永远不应该使用 shell 引用。正确的方法是不要使用 shell 引用,而是使用 subprocess.callsubprocess.Popen,并传递一个未引用参数列表。这对于 shell 展开来说是免疫的。

即:

subprocess.Popen(['echo', '"', '$foo'], shell=False)

如果您想取消引用 shell 引用的数据,可以像这样使用 shlex.shlex
list(shlex.shlex("hello stack 'overflow'\''s' quite cool"))

12
如果我需要通过 ssh 传递一个需要转义的命令,在它到达另一端后执行该命令,该怎么办? - Mike Boers
13
这不是一个有帮助的答案(好吧,它回答了我问题的一半,所以它是一半有用的...)。当你需要使用shell引用时,有许多场合需要这样做--Mike Boers提供了一个很棒的例子(事实上,那就是我遇到的问题)。 - YGA
事实上更糟糕的是,给定的示例代码出现错误:(Pdb) list(shlex.shlex("hello stack 'overflow'''s' quite cool")) *** 参数错误:'(shlex.shlex("hello stack 'overflow'\''s' quite cool"))' - YGA
1
来自子进程文档:如果 shell 参数为 True,建议将 args 作为字符串而不是序列传递。... 这包括例如用引号或反斜杠转义带有空格的文件名。-那只是需要使用引号的另一个例子。 - Seppo Enarvi
这个答案不够好,你可能正在生成一个 shell 脚本以供以后运行,而不是直接执行命令。 - ideasman42
5
我需要使用 shell quoting 的情况是:我的 Python 程序正在使用 subprocess.Popen(),但当它执行时,我希望它能够在控制台上打印出一些可以直接复制粘贴到 shell 中的内容,以便在命令失败时进行手动调试。 - user23614

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接