python - Ubuntu 18.04 上 Python 的 os.system 和 subprocess.check_output 中莫名其妙的 shell 命令取消转义行为

标签 python ubuntu subprocess ubuntu-18.04 os.system

我对 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/

相关文章:

python - 贪婪的 Python RegEx 捕获组包含 "and"

android - 从 Android Studio 运行时 Genymotion 找不到 VirtualBox

python - Systemd + 非根 Gunicorn 服务 = 不存在的子进程

linux - 如何通过 ssh 登录到 Ubuntu 并自动执行 sudo su?

python - 将参数从 python 程序传递给 shell 脚本

python从shell命令输出中读取

python - 仅返回尚未成为患者关系一部分的用户

python - 简化python中的selenium输出

javascript - 自然语言处理数据库查询

bash - 试图通过 shell 脚本终止进程