如何逃避os.system()调用?
使用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('`', '\`') )
即:我总是将参数用双引号引起来,然后用反斜杠将双引号内的特殊字符引起来。