我对 Python 在传递给 os.system
的命令中不转义反斜杠感到困惑。在 Ubuntu 18.04 上(在 CentOS 上一切正常)。考虑这个程序:
#!/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')
当我在 Ubuntu 18.04 上运行它时,我得到了这个:
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: \\\\
请注意,它在应该输出两个的地方输出一个反斜杠,在应该输出三个或四个的地方输出两个反斜杠!
但是,在我的 CentOS 7 机器上,一切正常。在两台机器上,shell 都是
/bin/bash
.这是脚本的python2.7调用的strace输出,以防万一:https://gist.githubusercontent.com/mbautin/a97cfb6f880860f5fe6ce1474b248cfd/raw我想从 Python 调用 shell 命令的最安全的行为是将它们写入临时脚本文件!
最佳答案
虽然我同意这种行为很奇怪,但这并不是莫名其妙的。该行为是有原因的,与 Python 或 subprocess
无关.在 C 程序中看到完全相同的行为,使用 system
与您的 Python 程序一样调用操作系统 (Linux)。
原因与您的 shell 有关,但与 bash
不完全一样。 .原因是在调用 os.system()
时或 subprocess.Popen()
家庭(包括 subprocess.check_output()
)与 shell=True
. documentation声明“在 shell=True 的 POSIX 上,shell 默认为/bin/sh。”因此,调用您的 echo
的 shell命令不是 bash
即使那是您的默认 shell 以及您从中运行脚本/启动 Python 的 shell。
相反,您的命令由 /bin/sh
执行你的系统。很长一段时间,这只是指向/bin/bash
(以 POSIX 兼容模式运行)在几乎所有 Linux 版本中,然而,最近这在一些发行版中发生了变化,其中包括 Ubuntu(但显然不是 CentOS,因为在那里你看不到相同的行为),现在有 /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
旗帜。或者,如果您需要某些特定于 shell 的功能,请自行控制 shell 的调用。并且,在这种特殊情况下,第三种方法是显式指向 /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
时命令应该是 list
而不是字符串。这可以是 shlex.split()如图所示。在这些方法中,首选第一种(直接
echo
调用),因为 security concerns ,如果某些参数有可能来自不受信任的来源。然而,在这种情况下,shlex.split()
也不应该使用,因为它会打开相同的安全漏洞。
关于python - Ubuntu 18.04 上 Python 的 os.system 和 subprocess.check_output 中莫名其妙的 shell 命令取消转义行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53124412/