如何逃避os.system()调用?

时间:2008-08-30 09:27:25

标签: python shell escaping

使用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()提供的快速简便的解决方案感兴趣,并且字符串的来源要么不是用户生成的,要么至少是由可信用户(我)输入。

10 个答案:

答案 0 :(得分:141)

shlex.quote()做了你想要的python 3。

(使用pipes.quote支持python 2和python 3)

答案 1 :(得分:75)

这就是我使用的:

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

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

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

答案 2 :(得分:54)

也许您有使用os.system()的具体原因。但如果不是,你应该使用subprocess module。您可以直接指定管道,避免使用shell。

以下内容来自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]

答案 3 :(得分:9)

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

答案 4 :(得分:4)

请注意,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 issue 7476;它已在Python 2.6和3.2及更新版本中修复。

答案 5 :(得分:3)

我相信os.system只是调用为用户配置的命令shell,所以我认为你不能以独立于平台的方式来做。我的命令shell可以是来自bash,emacs,ruby甚至quake3的任何东西。其中一些程序并不期望你传递给他们的那种论点,即使他们这样做也不能保证他们以同样的方式逃避。

答案 6 :(得分:3)

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

根据sourcepipes.quote()是一种“可靠地引用字符串作为 / bin / sh 的单个参数的方法” 。 (尽管它是deprecated since version 2.7并最终在Python 3.3中作为shlex.quote()函数公开公开。)

the other hand上,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)

答案 7 :(得分:1)

我使用的功能是:

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

就是:我总是将参数括在双引号中,然后反斜杠引用双引号中唯一的特殊字符。

答案 8 :(得分:-3)

如果您确实使用了系统命令,我会尝试将进入os.system()调用的内容列入白名单。例如..

clean_user_input re.sub("[^a-zA-Z]", "", user_input)
os.system("ls %s" % (clean_user_input))

子进程模块是一个更好的选择,我建议尽可能避免使用像os.system / subprocess这样的东西。

答案 9 :(得分:-3)

真正的答案是:首先不要使用os.system()。请改用subprocess.call并提供未转义的参数。