Хотя я могу согласиться с тем, что поведение странное, оно необъяснимо. Есть причина для поведения, которое не имеет ничего общего с Python или subprocess
. Точно такое же поведение наблюдается в программе на C, использующей вызов system
для ОС (Linux), как и в вашей программе на Python.
Причина связана с вашей оболочкой, но не совсем с bash
. Причина скорее в том, что при звонке os.system()
или семье subprocess.Popen()
(включая subprocess.check_output()
) с shell=True
. Документация гласит, что "В POSIX с shell = True оболочкой по умолчанию является /bin/sh." Таким образом, оболочка, которая вызывает вашу команду echo
, не является bash
даже если это ваша оболочка по умолчанию и оболочка, из которой вы запускаете свой скрипт / запускаете Python.
Вместо этого ваша команда выполняется /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
в не избежать обратной косой черты.
Так вот, это причина того, что вы видите. Хороший способ избежать этой проблемы - , а не полагаться на вызов системной оболочки. Если это одна команда, такая как echo
, то лучше вообще не вызывать оболочку, удаляя флаг 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
команда должна быть list
, а не строкой. Это может быть shlex.split () , как показано.
Из этих подходов предпочтителен первый (прямой echo
вызов) из-за соображений безопасности , если есть вероятность того, что некоторые параметры поступят из ненадежных источников. В этом случае, однако, shlex.split()
также не следует использовать, поскольку это открывает те же уязвимости безопасности.