使用os.system()时,通常需要转义文件名和其他作为参数传递给命令的参数。 我怎样才能做到这一点? 最好是可以在多个操作系统/外壳上运行的东西,尤其是bash。
我目前正在执行以下操作,但是请确保为此必须有一个库函数,或者至少是一个更优雅/更强大/更有效的选项:
1 2 3 4 5 6
| 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输入的 受信任的用户(我)。
自python 3起,shlex.quote()即可满足您的需求。
(使用pipes.quote同时支持python 2和python 3)
这是我用的:
1 2
| def shellquote(s):
return"'" + s.replace("'","'\''") +"'" |
外壳程序将始终接受带引号的文件名,并在将其传递给所涉及的程序之前删除其引号。值得注意的是,这避免了文件名包含空格或其他任何讨厌的shell元字符的问题。
更新:如果您使用的是Python 3.3或更高版本,请使用shlex.quote而不是自己滚动。
也许您有使用os.system()的特定原因。但是,如果没有,您可能应该使用subprocess模块。您可以直接指定管道,并避免使用外壳。
以下来自PEP324:
1 2 3 4 5 6 7 8
| 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] |
也许subprocess.list2cmdline是更好的镜头?
请注意,pipes.quote实际上在Python 2.5和Python 3.1中已损坏,并且不安全使用-它不处理零长度参数。
1 2 3 4
| >>> 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及更高版本中修复。
注意:这是Python 2.7.x的答案。
根据消息来源,pipes.quote()是"可靠地将字符串作为/ bin / sh的单个参数引用"的一种方法。 (尽管从2.7版开始不推荐使用,但最终在Python 3.3中作为shlex.quote()函数公开了。)
另一方面,subprocess.list2cmdline()是一种"使用与MS C运行时相同的规则将参数序列转换为命令行字符串"的方法。
在这里,我们提供了平台无关的方式来引用命令行字符串。
1 2 3 4 5 6 7 8 9 10 11 12
| 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) |
用法:
1 2 3 4 5 6
| # Quote a single argument
print quote_args(['my argument'])
# Quote multiple arguments
my_args = ['This', 'is', 'my arguments']
print quote_args(my_args) |
我相信os.system只会调用为用户配置的任何命令外壳,因此我认为您不能以独立于平台的方式进行操作。我的命令外壳可以是bash,emacs,ruby甚至quake3中的任何东西。这些程序中的某些程序并不期望您传递给它们的参数,即使它们这样做,也不能保证它们以相同的方式进行转义。
我使用的功能是:
1 2 3 4 5 6 7 8
| def quote_argument(argument):
return '"%s"' % (
argument
.replace('\', '\\\')
.replace('"', '\"')
.replace('$', '\\$')
.replace('`', '\\`')
) |
即:我总是将参数用双引号引起来,然后用反斜杠将双引号内的特殊字符引起来。
真正的答案是:首先不要使用os.system()。请使用subprocess.call并提供未转义的参数。
如果您确实使用了system命令,我将尝试将os.system()调用中的内容列入白名单。
1 2
| clean_user_input re.sub("[^a-zA-Z]","", user_input)
os.system("ls %s" % (clean_user_input)) |
subprocess模块??是一个更好的选择,我建议尽量避免使用os.system / subprocess之类的东西。