我对在Ubuntu 18.04上传递给blank=True
的命令中Python不能反斜杠转义感到困惑(在CentOS上一切正常)。考虑这个程序:
os.system
当我在Ubuntu 18.04上运行它时,我得到了:
#!/usr/bin/env python
import os
import sys
import subprocess
def get_command(n):
return "echo 'Should be %d backslashes: %s'" % (n, "\\" * n)
print("")
print("Using os.system directly:")
print("")
for n in range(1, 5):
os.system(get_command(n))
print("")
print("Using subprocess.check_output:")
print("")
for n in range(1, 5):
sys.stdout.write(subprocess.check_output(get_command(n), shell=True).decode('utf-8'))
print("")
print("Writing the bash code to a script and using os.system on the script:")
print("")
for n in range(1, 5):
with open('/tmp/script.sh', 'w') as f:
f.write(get_command(n))
os.system('/bin/bash /tmp/script.sh')
请注意,它输出一个反斜杠,应该输出两个反斜杠,并输出两个反斜杠,它应该输出三个或四个!
但是,在我的CentOS 7机器上,一切正常。在两台计算机上,外壳均为Using os.system directly:
Should be 1 backslashes: \
Should be 2 backslashes: \
Should be 3 backslashes: \\
Should be 4 backslashes: \\
Using subprocess.check_output:
Should be 1 backslashes: \
Should be 2 backslashes: \
Should be 3 backslashes: \\
Should be 4 backslashes: \\
Writing the bash code to a script and using os.system on the script:
Should be 1 backslashes: \
Should be 2 backslashes: \\
Should be 3 backslashes: \\\
Should be 4 backslashes: \\\\
。这是脚本的python2.7调用的strace输出,以防万一:https://gist.githubusercontent.com/mbautin/a97cfb6f880860f5fe6ce1474b248cfd/raw
我猜想从Python调用shell命令最安全的行为就是将它们写入临时脚本文件中!
答案 0 :(得分:3)
虽然我可以同意这种行为很奇怪,但这不是无法解释的。出现这种情况的原因与Python或subprocess
无关。在C程序中,使用system
对OS(Linux)的调用与在Python程序中完全一样。
虽然原因与您的shell有关,但与bash
并不完全相同。原因是,使用os.system()
来呼叫subprocess.Popen()
或subprocess.check_output()
家族(包括shell=True
)时。 documentation指出“在带shell = True的POSIX上,shell默认为/ bin / sh。” 因此,调用echo
命令的shell不是{{ 1}},即使这是您的默认外壳程序以及您从中运行脚本/启动Python的外壳程序。
相反,您的命令由系统的bash
执行。很长一段时间以来,这几乎指向所有Linux版本中的/bin/sh
(以POSIX兼容模式运行),但是,最近在某些发行版中已经改变了,其中包括Ubuntu(显然不是CentOS,因为您没有这样做)。在那里看到相同的行为),现在/bin/bash
指向/bin/sh
了:
bin/dash
因此,您的脚本实际上是由$ ll /bin/sh
lrwxrwxrwx 1 root root 4 sep 23 12:53 /bin/sh -> dash*
而不是dash
执行的。 “为了提高效率”(请参见提示符bash
)man dash
已选择在内部实现dash
,而不是使用echo
(由/bin/echo
使用)。不幸的是,bash
dash
不如echo
强大,并且对字符串输入有不同的解释,即/bin/echo
dash
会转义许多反斜杠命令,实际上意味着它“吞咽”
还有一个反斜杠。
通过指定echo
选项(请参阅/bin/echo
)可以使-e
以相同的方式运行,但不幸的是,不可能有man echo
内置dash
来不转义反斜杠。
现在,这就是您看到的原因。避免该问题的一个好方法是不依赖系统外壳程序调用。如果它是单个命令,例如echo
,则最好根本不调用Shell,而删除echo
标志。或者,如果您需要某些特定于Shell的功能,请自己控制外壳的调用。并且,在这种特殊情况下,第三种方法是在执行时显式指向shell=True
,因为这样可以确保使用“标准” /bin/echo
:
echo
请注意,在不使用#!/usr/bin/env python3
import sys
import subprocess
import shlex
def get_command(n):
return "echo 'Should be {} backslahes: {}'".format(n, "\\"*n)
print("")
print("Using subprocess.check_output:")
print("")
for n in range(1, 5):
# Direct invocation:
cmd = get_command(n)
sys.stdout.write(subprocess.check_output(shlex.split(cmd)).decode())
# Controlling invocation shell:
bash_cmd = ['/bin/bash', '-c'] + [cmd]
sys.stdout.write(subprocess.check_output(bash_cmd).decode())
# Using shell=True but point to /bin/echo
echo_cmd = '/bin/' + cmd
sys.stdout.write(subprocess.check_output(echo_cmd, shell=True).decode())
的情况下,该命令应为shell=True
而不是字符串。如图所示,可以为shlex.split()。
在这些方法中,如果有可能某些参数来自不受信任的来源,则首选第一种方法(直接调用list
,这是由于security concerns)。但是,在那种情况下,echo
也不应使用,因为它会带来相同的安全漏洞。