Вопрос-Ответ

Why does passing variables to subprocess.Popen not work despite passing a list of arguments?

Почему передача переменных в subprocess.Popen не работает, несмотря на передачу списка аргументов?

У меня есть скрипт, который вызывает другой скрипт Python с помощью subprocess.Popen. Но поскольку у меня есть аргументы, хранящиеся в переменных)

servers[server]['address']
servers[server]['port']
servers[server]['pass']

Я не могу выполнить команду

p = subprocess.Popen(
["python mytool.py -a ", servers[server]['address'], "-x", servers[server]['port'], "-p", servers[server]['pass'], "some additional command"],
shell=True,
stdout=subprocess.PIPE
)
Переведено автоматически
Ответ 1

Удаление shell=True. Аргументы в Popen() обрабатываются по-разному в Unix, если shell=True:

import sys
from subprocess import Popen, PIPE

# populate list of arguments
args = ["mytool.py"]
for opt, optname in zip("-a -x -p".split(), "address port pass".split()):
args.extend([opt, str(servers[server][optname])])
args.extend("some additional command".split())

# run script
p = Popen([sys.executable or 'python'] + args, stdout=PIPE)
# use p.stdout here...
p.stdout.close()
p.wait()

Обратите внимание, что передача shell=True для команд с внешним вводом представляет угрозу безопасности, как описано в предупреждении в документации.

Ответ 2

При вызове subprocess.Popen вы можете передать либо строку, либо список для выполнения команды. Если вы передаете список, элементы должны быть разделены определенным образом.

В вашем случае вам нужно разделить его примерно так:

command = ["python",  "mytool.py", "-a", servers[server]['address'], 
"-x", servers[server]['port'],
"-p", servers[server]['pass'],
"some", "additional", "command"]
p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)

Это потому, что если вы передаете список, Popen предполагается, что вы уже разделили командную строку на слова (значения, которые в конечном итоге будут в sys.argv), поэтому в этом нет необходимости.

То, как вы его вызываете, попытается запустить двоичный файл с именем "python mytool.py -a", что не то, что вы имели в виду.

Другой способ исправить это - объединить все слова в строку (которая Popen затем разделится - см. subprocess.list2cmdline). Но вам лучше использовать версию списка, если это возможно - это упрощает управление разделением командной строки (например, если аргументы содержат пробелы или кавычки) без необходимости возиться с заключением символов в кавычки.

Ответ 3

Ваша проблема в типе str для первого Popen аргумента. Замените его на list. Приведенный ниже код может работать:

address = servers[server]['address']
port = servers[server]['port']
pass = servers[server]['pass']

command = "python mytool.py -a %s -x %d -p %s some additional command" % (address, port, pass)
p = subprocess.Popen(command.split(), stdout=subprocess.PIPE)
# it is a list^^^^^^^^^^^^^^^ shell=False

Если command аргументы получены из надежного источника, вы можете сконструировать command и использовать его с shell=True таким образом:

import pipes as p
command = "python mytool.py -a {} -x {} -p {} some additional command".format(p.quote(address), p.quote(port), p.quote(pass))
p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)

Примечание 1: создание command с shell=True помощью потенциально небезопасно. Используйте pipes.quote() для уменьшения возможности внедрения.

Примечание 2: pipes.quote() устарел с тех пор, как python2; для python3 использования shlex модуля.

Ответ 4

Вы должны объединить команду со всей строкой:

p = subprocess.Popen("python mytool.py -a " + servers[server]['address'] + " -x " + servers[server]['port'] + " -p " + servers[server]['pass'] + " some additional command", shell=True, stdout=subprocess.PIPE)
python subprocess