Как использовать подпроцесс, когда несколько аргументов содержат пробелы?

Я работаю над сценарием-оболочкой, который будет выполнять исполняемый файл vmware, позволяя автоматизировать действия по запуску/выключению/регистрации/отмене регистрации виртуальной машины. Я пытаюсь использовать подпроцесс для обработки вызова исполняемого файла, но пробелы в пути к исполняемому файлу и в параметрах исполняемого файла не обрабатываются правильно подпроцессом. Ниже фрагмент кода:

vmrun_cmd = r"c:/Program Files/VMware/VMware Server/vmware-cmd.bat"
def vm_start(target_vm):
    list_arg = "start"
    list_arg2 = "hard"
    if vm_list(target_vm):
            p = Popen([vmrun_cmd, target_vm, list_arg, list_arg2],   stdout=PIPE).communicate()[0]
            print p
    else:
            vm_register(target_vm)
            vm_start(target_vm)
def vm_list2(target_vm):
    list_arg = "-l"
    p = Popen([vmrun_cmd, list_arg], stdout=PIPE).communicate()[0]
    for line in p.split('\n'):
            print line

Если я вызову функцию vm_list2, я получу следующий вывод:

$ ./vmware_control.py --list                                                
C:\Virtual Machines\QAW2K3Server\Windows Server 2003 Standard Edition.vmx
C:\Virtual Machines\ubunturouter\Ubuntu.vmx
C:\Virtual Machines\vacc\vacc.vmx
C:\Virtual Machines\EdgeAS-4.4.x\Other Linux 2.4.x kernel.vmx
C:\Virtual Machines\UbuntuServer1\Ubuntu.vmx
C:\Virtual Machines\Other Linux 2.4.x kernel\Other Linux 2.4.x kernel.vmx
C:\Virtual Machines\QAClient\Windows XP Professional.vmx

Если я вызову функцию vm_start, для которой требуется параметр path-to-vm, я получу следующий вывод:

$ ./vmware_control.py --start "C:\Virtual Machines\ubunturouter\Ubuntu.vmx"
'c:\Program' is not recognized as an internal or external command,
operable program or batch file.

По-видимому, наличие второго параметра со встроенными пробелами меняет способ интерпретации подпроцессом первого параметра. Любые предложения о том, как решить эту проблему?

python2.5.2/cygwin/winxp


person Community    schedule 30.04.2009    source источник
comment
Почему ваши косые черты в c:/Program Files/VMware/VMware Server/vmware-cmd.bat идут не так? Разве это не c:\Program Files\... ?   -  person S.Lott    schedule 30.04.2009
comment
Что ж, cygwin - это порт * nix, поэтому, похоже, ему нравится стандартная (или то, что я понимаю как стандарт) нотация косой черты * nix. Насколько я понимаю, подпроцесс должен переводить разделитель во все, что нужно базовой системе.   -  person    schedule 30.04.2009
comment
это решилось на данный момент?   -  person Gohan    schedule 25.04.2013


Ответы (10)


Если у вас есть пробелы в пути, самый простой способ, который я нашел, чтобы правильно их интерпретировать, - это.

subprocess.call('""' + path + '""')

Я не знаю, почему именно ему нужны двойные двойные кавычки, но это то, что работает.

person Tofystedeth    schedule 30.04.2009

'c:\Program' is not recognized as an internal or external command,
operable program or batch file.

Чтобы получить это сообщение, вы:

  1. Использование shell=True:

    vmrun_cmd = r"c:\Program Files\VMware\VMware Server\vmware-cmd.bat"
    subprocess.Popen(vmrun_cmd, shell=True)
    
  2. Изменение vmrun_cmd в другой части вашего кода

  3. Получение этой ошибки из чего-то внутри vmware-cmd.bat

Что попробовать:

  • Откройте приглашение Python, выполните следующую команду:

    subprocess.Popen([r"c:\Program Files\VMware\VMware Server\vmware-cmd.bat"])
    

Если это сработает, то о цитировании не может быть и речи. Если нет, вы изолировали проблему.

person nosklo    schedule 30.04.2009
comment
По порядку: 1: Я явно не устанавливал shell=True, так что это не так. 2: vmrun_cmd — это глобальная константа, которая каждый раз используется одинаково. 3: Нет. Исполняемый файл еще не был вызван к моменту возникновения ошибки — это начало строки, указывающей его путь. - person ; 30.04.2009

Я считаю, что list2cmdline(), который выполняет обработку ваших аргументов списка, разбивает любой строковый аргумент на пробел, если строка не содержит двойных кавычек. Так что я бы ожидал

vmrun_cmd = r'"c:/Program Files/VMware/VMware Server/vmware-cmd.bat"'

быть тем, кем ты хочешь.

Вы также, вероятно, захотите заключить другие аргументы (например, target_vm) в двойные кавычки, предполагая, что каждый из них также представляет отдельный аргумент для представления в командной строке. Что-то типа

r'"%s"' % target_vm

(например) должно подойти.

См. документацию list2cmdline.

D'A

person darch    schedule 30.04.2009
comment
Что ж, target_vm — это аргумент, а не константа, так как же лучше использовать двойные кавычки в этой ситуации? - person ; 30.04.2009
comment
Я бы, наверное, сформулировал это как r'%s' % target_vm - person darch; 30.04.2009
comment
Блин, это тяжело читать в комментарии. Перенесем на ответ. - person darch; 30.04.2009
comment
Ссылка на документацию list2cmdline не работает, и эта вещь едва задокументирована на docs.python.org/ 2.6/library/subprocess.html — обратите внимание, что это документ для версии 2.6, и с тех пор он был удален из docs.python.org/2.7/library/subprocess.html - person Davide; 01.03.2016

В Python для MS Windows класс subprocess.Popen использует API CreateProcess для запуска процесса. CreateProcess принимает строку, а не что-то вроде массива аргументов. Python использует subprocess.list2cmdline для преобразования списка аргументов в строку для CreateProcess.

На вашем месте я бы посмотрел, что возвращает subprocess.list2cmdline(args) (где args — первый аргумент Popen). Было бы интересно посмотреть, заключает ли он кавычки вокруг первого аргумента.

Конечно, это объяснение может не применяться в среде Cygwin.

Сказав все это, у меня нет MS Windows.

person Warren    schedule 30.04.2009

Одна проблема заключается в том, что если команда заключена в кавычки и не содержит пробелов, это также может запутать оболочку.

Итак, я делаю это:

if ' ' in raw_cmd:
    fmt = '"%s"'
else:
    fmt = '%s'

cmd = fmt % raw_cmd
person Ryan Ginstrom    schedule 14.06.2009

Это была довольно сложная проблема для последних трех наших... ничего из заявленного до сих пор не работало, ни использование r"", ни Popen со списком и так далее. В конце концов сработала комбинация строки формата и r"". Итак, мое решение таково:

subprocess.Popen("{0} -f {1}".format(pathToExe, r'"%s"' % pathToVideoFileOrDir))

где обе переменные pathToExe и pathToVideoFileOrDir имеют пробелы в своем пути. Использование \" в отформатированной строке не сработало и привело к той же ошибке, что первый путь больше не определяется правильно.

person Michael Burkhardt    schedule 08.04.2016

2 вещи

1)

Вероятно, вы не хотите использовать Pipe. Если выходные данные подпрограммы превышают 64 КБ, скорее всего, ваш процесс рухнет. http://thraxil.org/users/anders/posts/2008/03/13/Subprocess-Hanging-PIPE-is-your-enemy/

2) Subprocess.Popen имеет оболочку аргумента ключевого слова, что делает ее такой, как если бы оболочка анализировала ваши аргументы, установка shell=True должна делать то, что вы хотите.

person Community    schedule 30.04.2009

Почему вы используете р""? Я считаю, что если вы удалите «r» в начале, она будет рассматриваться как стандартная строка, которая может содержать пробелы. Затем Python должен правильно заключать строку в кавычки при отправке ее в оболочку.

person Justin    schedule 30.04.2009
comment
Я проверил это, и статус необработанной строки не меняет поведение. - person ; 30.04.2009
comment
Имеет смысл использовать r...\... для имен файлов Windows. - person S.Lott; 30.04.2009

Возможно, глупое предложение, но, возможно, попробуйте следующее, чтобы удалить подпроцесс + пробелы из уравнения:

import os
from subprocess Popen, PIPE

os.chdir(
    os.path.join("C:", "Program Files", "VMware", "VMware Server")
)

p = Popen(
    ["vmware-cmd.bat", target_vm, list_arg, list_arg2],
    stdout=PIPE
).communicate()[0]

Может тоже стоит попробовать..

p = Popen(
    [os.path.join("C:", "Program Files", "VMware", "VMware Server", "vmware-cmd.bat"), ...
person dbr    schedule 30.04.2009

Вот что мне не нравится

vmrun_cmd = r"c:/Program Files/VMware/VMware Server/vmware-cmd.bat"

У вас есть пробелы в имени самой команды, что сбивает с толку вашу оболочку. Следовательно, «c:\Program» не распознается как внутренняя или внешняя команда, исполняемая программа или пакетный файл».

Вариант 1 — поместите файл .BAT в другое место. Действительно, поместите все свои VMWare в другое место. Вот правило: Ни для чего не использовать каталог Program Files. Это просто неправильно.

Вариант 2 - указать значение vmrun_cmd

vmrun_cmd = r'"c:/Program Files/VMware/VMware Server/vmware-cmd.bat"'
person S.Lott    schedule 30.04.2009