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

时间:2020-03-05 18:45:23  来源:igfitidea点击:

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

解决方案

回答

我相信os.system只会调用为用户配置的任何命令外壳,因此我认为我们不能以独立于平台的方式进行操作。我的命令外壳可以是bash,emacs,ruby甚至quake3中的任何东西。这些程序中的某些程序并不期望我们传递给它们的参数,即使它们这样做,也不能保证它们以相同的方式进行转义。

回答

这是我用的:

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

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

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

回答

也许我们有使用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]

回答

如果我们确实使用了system命令,我将尝试将os.system()调用中的内容列入白名单。

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

subprocess模块​​是一个更好的选择,我建议尽可能避免使用os.system / subprocess之类的东西。

回答

自python 3以来,shlex.quote()可以满足需求。

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

回答

请注意,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及更高版本中修复。

回答

我使用的功能是:

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

即:我总是将参数用双引号引起来,然后用反斜杠将双引号内的特殊字符引起来。