题
使用 os.system() 时,通常需要转义文件名和其他作为参数传递给命令的参数。我怎样才能做到这一点?最好是可以在多个操作系统/shell 上运行的东西,尤其是 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() 提供的快速且简单的解决方案感兴趣,并且字符串的源不是用户生成的,或者至少是由用户输入的受信任的用户(我)。
解决方案
这就是我使用的:
def shellquote(s):
return "'" + s.replace("'", "'\\''") + "'"
shell 将始终接受带引号的文件名,并在将其传递给相关程序之前删除周围的引号。值得注意的是,这避免了包含空格或任何其他令人讨厌的 shell 元字符的文件名问题。
更新: :如果您使用的是 Python 3.3 或更高版本,请使用 shlex.quote 而不是自己滚动。
其他提示
shlex.quote()
从 python 3 开始做你想做的事。
(使用 pipes.quote
同时支持 python 2 和 python 3)
也许您有特定的使用原因 os.system()
. 。但如果没有,你可能应该使用 subprocess
模块. 。您可以直接指定管道并避免使用 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]
或许 subprocess.list2cmdline
是更好的镜头吗?
请注意,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 及更高版本中修复。
我相信 os.system 只是调用为用户配置的任何命令 shell,所以我认为您不能以独立于平台的方式来完成它。我的命令 shell 可以是 bash、emacs、ruby 甚至 quake3 中的任何命令。其中一些程序并不期望您传递给它们的参数类型,即使它们期望,也不能保证它们会以相同的方式进行转义。
注意: :这是 Python 2.7.x 的答案。
根据 来源, pipes.quote()
是一种方法“可靠地引用字符串作为单个参数 /bin/sh”。(虽然这是 自 2.7 版本起已弃用 并最终在 Python 3.3 中公开暴露为 shlex.quote()
功能。)
在 另一方面, 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)
我使用的功能是:
def quote_argument(argument):
return '"%s"' % (
argument
.replace('\\', '\\\\')
.replace('"', '\\"')
.replace('$', '\\$')
.replace('`', '\\`')
)
那是:我总是将参数括在双引号中,然后反斜杠引用双引号内唯一的特殊字符。
如果您确实使用系统命令,我会尝试将 os.system() 调用中的内容列入白名单。例如..
clean_user_input re.sub("[^a-zA-Z]", "", user_input)
os.system("ls %s" % (clean_user_input))
subprocess 模块是一个更好的选择,我建议尽可能避免使用 os.system/subprocess 之类的东西。
真正的答案是:不要使用 os.system()
首先。使用 subprocess.call
相反并提供未转义的参数。