135

使用 os.system() 时,通常需要转义作为参数传递给命令的文件名和其他参数。我怎样才能做到这一点?最好是可以在多个操作系统/外壳上运行的东西,尤其是对于 bash。

我目前正在执行以下操作,但我确信必须为此提供一个库函数,或者至少是一个更优雅/强大/高效的选项:

def sh_escape(s):
   return s.replace("(","\\(").replace(")","\\)").replace(" ","\\ ")

os.system("cat %s | grep something | sort > %s" 
          % (sh_escape(in_filename), 
             sh_escape(out_filename)))

编辑:我已经接受了使用引号的简单答案,不知道为什么我没有想到这一点;我猜是因为我来自 Windows,其中 ' 和 " 的行为略有不同。

关于安全性,我理解这种担忧,但在这种情况下,我对 os.system() 提供的快速简便的解决方案感兴趣,并且字符串的源不是用户生成的,或者至少是由受信任的用户(我)。

4

9 回答 9

168

shlex.quote()从 python 3 开始做你想做的事。

(用于pipes.quote同时支持python 2和python 3)

于 2009-05-11T12:06:40.117 回答
91

这就是我使用的:

def shellquote(s):
    return "'" + s.replace("'", "'\\''") + "'"

在将文件名传递给相关程序之前,shell 将始终接受带引号的文件名并删除周围的引号。值得注意的是,这避免了包含空格或任何其他讨厌的 shell 元字符的文件名的问题。

更新:如果您使用的是 Python 3.3 或更高版本,请使用shlex.quote而不是自己滚动。

于 2008-08-30T10:13:11.503 回答
62

也许您有使用os.system(). 但如果不是,您可能应该使用该subprocess模块。您可以直接指定管道并避免使用外壳。

以下来自PEP324

Replacing shell pipe line
-------------------------

output=`dmesg | grep hda`
==>
p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
output = p2.communicate()[0]
于 2008-08-30T10:15:02.403 回答
12

也许subprocess.list2cmdline是一个更好的镜头?

于 2012-05-25T07:54:41.123 回答
6

请注意,pipes.quote 实际上在 Python 2.5 和 Python 3.1 中被破坏并且使用不安全——它不处理零长度参数。

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

Python 问题 7476;它已在 Python 2.6 和 3.2 及更高版本中修复。

于 2009-12-10T23:03:55.737 回答
4

我相信 os.system 只是调用为用户配置的任何命令 shell,所以我认为你不能以独立于平台的方式来执行它。我的命令 shell 可以是 bash、emacs、ruby 甚至是 quake3。其中一些程序并不期待您传递给他们的那种参数,即使他们这样做了,也不能保证他们会以同样的方式进行转义。

于 2008-08-30T09:43:50.123 回答
3

注意:这是 Python 2.7.x 的答案。

根据消息来源pipes.quote()这是一种“可靠地将字符串引用为/bin/sh的单个参数”的方法。(尽管它自 2.7 版以来已被弃用shlex.quote(),并最终在 Python 3.3 中作为函数公开。)

另一方面,subprocess.list2cmdline()一种“使用与MS C 运行时相同的规则将一系列参数转换为命令行字符串”的方法。

我们在这里,为命令行引用字符串的平台无关方式。

import sys
mswindows = (sys.platform == "win32")

if mswindows:
    from subprocess import list2cmdline
    quote_args = list2cmdline
else:
    # POSIX
    from pipes import quote

    def quote_args(seq):
        return ' '.join(quote(arg) for arg in seq)

用法:

# Quote a single argument
print quote_args(['my argument'])

# Quote multiple arguments
my_args = ['This', 'is', 'my arguments']
print quote_args(my_args)
于 2015-04-13T03:26:35.380 回答
1

我使用的功能是:

def quote_argument(argument):
    return '"%s"' % (
        argument
        .replace('\\', '\\\\')
        .replace('"', '\\"')
        .replace('$', '\\$')
        .replace('`', '\\`')
    )

也就是说:我总是用双引号将参数括起来,然后用反斜杠引用双引号内唯一的特殊字符。

于 2010-10-03T21:21:44.853 回答
0

在像 Bash 这样的 UNIX shell 上,您可以shlex.quote在 Python 3 中使用来转义 shell 可能解释的特殊字符,例如空格和*字符:

import os
import shlex

os.system("rm " + shlex.quote(filename))

但是,出于安全目的,这还不够!您仍然需要注意不要以意外的方式解释命令参数。例如,如果文件名实际上是类似的路径../../etc/passwd怎么办?当您只希望它删除在当前目录中找到的文件名时,运行os.system("rm " + shlex.quote(filename))可能会删除!/etc/passwd这里的问题不在于 shell 解释特殊字符,而是文件名参数没有被解释rm为简单的文件名,它实际上被解释为路径。

或者,如果有效的文件名以破折号开头,例如,-f怎么办?仅传递转义的文件名是不够的,您需要禁用选项 using--或者您需要传递不以破折号开头的路径,例如./-f. 这里的问题不在于 shell 解释特殊字符,而是rm命令将参数解释为文件名路径选项(如果它以破折号开头)。

这是一个更安全的实现:

if os.sep in filename:
     raise Exception("Did not expect to find file path separator in file name")

os.system("rm -- " + shlex.quote(filename))
于 2022-01-11T15:36:01.220 回答