问题:如何逃避os.system()调用?
使用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()提供的一种快速简便的解决方案感兴趣,并且字符串的来源不是用户生成的,或者至少是由受信任的用户(我)。
When using os.system() it’s often necessary to escape filenames and other arguments passed as parameters to commands. How can I do this? Preferably something that would work on multiple operating systems/shells but in particular for bash.
I’m currently doing the following, but am sure there must be a library function for this, or at least a more elegant/robust/efficient option:
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)))
Edit: I’ve accepted the simple answer of using quotes, don’t know why I didn’t think of that; I guess because I came from Windows where ‘ and ” behave a little differently.
Regarding security, I understand the concern, but, in this case, I’m interested in a quick and easy solution which os.system() provides, and the source of the strings is either not user-generated or at least entered by a trusted user (me).
回答 0
这是我用的:
def shellquote(s):
return "'" + s.replace("'", "'\\''") + "'"
外壳程序将始终接受带引号的文件名,并在将其传递给相关程序之前删除引号。值得注意的是,这避免了文件名包含空格或其他任何讨厌的shell元字符的问题。
更新:如果您使用的是Python 3.3或更高版本,请使用shlex.quote而不是自己滚动。
This is what I use:
def shellquote(s):
return "'" + s.replace("'", "'\\''") + "'"
The shell will always accept a quoted filename and remove the surrounding quotes before passing it to the program in question. Notably, this avoids problems with filenames that contain spaces or any other kind of nasty shell metacharacter.
Update: If you are using Python 3.3 or later, use shlex.quote instead of rolling your own.
回答 1
回答 2
也许您有使用的特定原因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]
Perhaps you have a specific reason for using os.system()
. But if not you should probably be using the subprocess
module. You can specify the pipes directly and avoid using the shell.
The following is from 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
也许subprocess.list2cmdline
是更好的选择?
Maybe subprocess.list2cmdline
is a better shot?
回答 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问题7476 ; 它已在Python 2.6和3.2及更高版本中修复。
Note that pipes.quote is actually broken in Python 2.5 and Python 3.1 and not safe to use–It doesn’t handle zero-length arguments.
>>> from pipes import quote
>>> args = ['arg1', '', 'arg3']
>>> print 'mycommand %s' % (' '.join(quote(arg) for arg in args))
mycommand arg1 arg3
See Python issue 7476; it has been fixed in Python 2.6 and 3.2 and newer.
回答 5
注意:这是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)
Notice: This is an answer for Python 2.7.x.
According to the source, pipes.quote()
is a way to “Reliably quote a string as a single argument for /bin/sh“. (Although it is deprecated since version 2.7 and finally exposed publicly in Python 3.3 as the shlex.quote()
function.)
On the other hand, subprocess.list2cmdline()
is a way to “Translate a sequence of arguments into a command line string, using the same rules as the MS C runtime“.
Here we are, the platform independent way of quoting strings for command lines.
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)
Usage:
# Quote a single argument
print quote_args(['my argument'])
# Quote multiple arguments
my_args = ['This', 'is', 'my arguments']
print quote_args(my_args)
回答 6
我相信os.system只会调用为用户配置的任何命令外壳,因此我认为您不能以与平台无关的方式进行操作。我的命令外壳可以是bash,emacs,ruby甚至quake3中的任何东西。这些程序中的某些程序并不期望您传递给它们的参数的种类,即使它们这样做了,也无法保证它们以相同的方式进行转义。
I believe that os.system just invokes whatever command shell is configured for the user, so I don’t think you can do it in a platform independent way. My command shell could be anything from bash, emacs, ruby, or even quake3. Some of these programs aren’t expecting the kind of arguments you are passing to them and even if they did there is no guarantee they do their escaping the same way.
回答 7
我使用的功能是:
def quote_argument(argument):
return '"%s"' % (
argument
.replace('\\', '\\\\')
.replace('"', '\\"')
.replace('$', '\\$')
.replace('`', '\\`')
)
即:我总是将参数用双引号引起来,然后用反斜杠将双引号内的特殊字符引起来。
The function I use is:
def quote_argument(argument):
return '"%s"' % (
argument
.replace('\\', '\\\\')
.replace('"', '\\"')
.replace('$', '\\$')
.replace('`', '\\`')
)
that is: I always enclose the argument in double quotes, and then backslash-quote the only characters special inside double quotes.
回答 8
如果您确实使用了system命令,我将尝试将os.system()调用中的内容列入白名单。
clean_user_input re.sub("[^a-zA-Z]", "", user_input)
os.system("ls %s" % (clean_user_input))
子进程模块是一个更好的选择,我建议尽量避免使用os.system / subprocess之类的东西。
If you do use the system command, I would try and whitelist what goes into the os.system() call.. For example..
clean_user_input re.sub("[^a-zA-Z]", "", user_input)
os.system("ls %s" % (clean_user_input))
The subprocess module is a better option, and I would recommend trying to avoid using anything like os.system/subprocess wherever possible.
回答 9
The real answer is: Don’t use os.system()
in the first place. Use subprocess.call
instead and supply the unescaped arguments.