使用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输入的 受信任的用户(我)。
-
当心安全问题! 例如,如果out_filename是foo.txt; rm -rf /恶意用户可以添加更多由Shell直接解释的命令。
-
在没有子进程甚至不是选项的情况下,这在没有os.system的情况下也很有用; 例如 生成外壳脚本。
-
理想的sh_escape函数可以通过简单地创建名为foo.txt\;\ rm\ -rf\ 之类的文件来转出;和空格并消除安全问题。
-
在几乎所有情况下,都应该使用子进程,而不是os.system。 调用os.system只是要求进行注入攻击。
自python 3起,shlex.quote()即可满足您的需求。
(使用pipes.quote同时支持python 2和python 3)
-
还有commands.mkarg。它也增加了一个引号空间(引号外),这可能是不希望的,也可能是不希望的。有趣的是它们的实现彼此之间是多么不同,并且比Greg Hewgills回答的要复杂得多。
-
由于某些原因,管道模块的标准库文档未提及pipes.quote
-
两者均未记录在案; command.mkarg在3.x中已弃用并删除,而pipe.quote仍然保留。
-
更正:在3.3中正式记录为shlex.quote(),出于兼容性考虑,保留了pipes.quote()。 [bugs.python.org/issue9723]
-
管道在Windows上不起作用-添加由双引号插入的单引号。
这是我用的:
1 2
| def shellquote(s):
return"'" + s.replace("'","'\''") +"'" |
外壳程序将始终接受带引号的文件名,并在将其传递给所涉及的程序之前删除其引号。值得注意的是,这避免了文件名包含空格或其他任何讨厌的shell元字符的问题。
更新:如果您使用的是Python 3.3或更高版本,请使用shlex.quote而不是自己滚动。
-
转义的单引号在单引号内无效。
-
@pixelbeat:这就是为什么他关闭单引号,添加转义的文字单引号,然后再次重新打开单引号的原因。
-
尽管这几乎不是shellquote函数的职责,但有趣的是,如果在该函数的返回值之前出现未加引号的反斜杠,则仍然会失败。士气:确保在安全可靠的代码中使用此代码(例如,硬编码命令的一部分),请勿将其附加到其他未引用的用户输入中。
-
请注意,除非您绝对需要shell功能,否则可能应该使用Jamies建议。
-
与此类似的东西现在可以作为shlex.quote正式获得。
-
@lhunath在两个方面都完全错误。这是为POSIX shell逃避一个单词的完美而正确的方法(请注意单词"一个单词",命令结构取决于用户,但微不足道)。通过列表传递无济于事在ssh命令之后,因为您需要转义多个级别,然后…
-
与shlex或pipes相比,此答案中提供的功能在shell引用方面做得更好。那些python模块错误地认为特殊字符是唯一需要引用的字符,这意味着当不希望出现这种情况时,将解析shell关键字(如time,case或while)。出于这个原因,我建议在此答案中使用单引号例程,因为它不会尝试变得"聪明",所以不会出现那些愚蠢的情况。
-
该解决方案无法解决的问题是文件名中是否存在/嵌入。 Shell会对此表示反对,因为它不允许在路径名组件中使用该字符。
-
在python 2.7中也存在-docs.python.org/2.7/library/shlex.html
-
我相信"" + s.replace("","""") +""的效果更好,因为它不需要反斜杠在shell中的双引号之前被解释为特殊。
也许您有使用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(尤其是check_call等)通常具有明显的优越性,但是在某些情况下,外壳转义仍然有用。 Im遇到的主要问题是Im必须调用ssh远程命令。
-
@CraigRinger,是的,ssh远程处理将我带到了这里。 :P我希望ssh在这里能有所帮助。
-
@ JrgenA.Erhard它没有--execvp-remote选项(或默认情况下以这种方式工作),这似乎很奇怪。通过外壳执行所有操作似乎很笨拙且冒险。 OTOH,ssh充满奇怪的怪癖,通常是在狭义的"安全性"视角下进行的工作,导致人们提出了更为不安全的解决方法。
也许subprocess.list2cmdline是更好的镜头?
-
看起来不错。有趣的是没有文档...(至少在docs.python.org/library/subprocess.html中)
-
它不能正确转义:subprocess.list2cmdline(["",,"\","])给出"" \ "
-
它不会逃脱外壳扩展符号
-
subprocess.list2cmdline()是否仅适用于Windows?
-
@JS是,list2cmdline符合Windows cmd.exe语法(请参阅Python源代码中的函数docstring)。 shlex.quote符合Unix bourne shell语法,但是通常不需要,因为Unix对直接传递参数提供了很好的支持。 Windows几乎要求您传递带有所有参数的单个字符串(因此需要适当的转义)。
请注意,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.6版似乎产生了正确的输出:mycommand arg1 arg3(这是两个单引号,尽管Stack Overflow上的字体很难分辨!)
-
2.6对我有用(bugs.python.org/issue7476),3.1返回空字符串。
注意:这是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中的任何东西。这些程序中的某些程序并不期望您传递给它们的参数,即使它们这样做,也不能保证它们以相同的方式进行转义。
-
期望一个基本或完全兼容POSIX的外壳(至少在Windows以外的任何地方,无论如何都知道您拥有的"外壳")并非不合理。 os.system不使用$ SHELL,至少在这里不使用。
我使用的功能是:
1 2 3 4 5 6 7 8
| def quote_argument(argument):
return '"%s"' % (
argument
.replace('\', '\\\')
.replace('"', '\"')
.replace('$', '\\$')
.replace('`', '\\`')
) |
即:我总是将参数用双引号引起来,然后用反斜杠将双引号内的特殊字符引起来。
-
请注意,您应该使用",\ $和`,否则转义不会发生。
-
另外,在某些(奇怪的)语言环境中使用双引号存在一些问题。建议的修复程序使用了pipes.quote,@ JohnWiseman指出该错误也已损坏。因此,格雷格·休吉尔(Greg Hewgill)的答案就是使用的答案。 (也是常规情况下炮弹内部使用的炮弹。)
真正的答案是:首先不要使用os.system()。请使用subprocess.call并提供未转义的参数。
-
该问题包含一个示例,其中子流程刚刚失败。如果可以使用子流程,则应该确保。但是,如果您不能...子流程不是万能的解决方案。哦,您的回答根本无法回答问题。
-
@ JrgenA.Erhard OP的示例不会因为他要使用外壳管道而失败吗?您应该始终使用子进程,因为它不使用外壳程序。这只是一个例子,但是您可以在本机子进程中进行管道处理,有一些pypi软件包试图使之更容易。我倾向于尽可能多地在python中进行所需的后处理。您始终可以制作自己的StringIO缓冲区,并通过子流程完全控制事情。
如果您确实使用了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之类的东西。