Использование подпроцесса.запуск с аргументами, содержащими кавычки

#python #python-3.x

#python #python-3.x

Вопрос:

Команда, которую я пытаюсь запустить, выглядит следующим образом:

 xvfb-run --auto-servernum --server-args="-screen 0 640x480x24" --error-file=/dev/stdout /opt/myExecutable
  

Это то, что у меня есть в Python3:

 args = ['xvfb-run', '--auto-servernum','--server-args="-screen 0 640x480x24"', '--error-file=/dev/stdout', '/opt/myExecutable']
command = ' '.join(xvfbArgs)
print(f'Command: {command}')
subprocess.run(xvfbArgs)
  

Я получаю следующее:

 Unrecognized option: "-screen
use: X [:<display>] [option]
...
segfault
...
Command: xvfb-run --auto-servernum --server-args="-screen 0 640x480x24" --error-file=/dev/stdout /opt/myExecutable
  

Напечатанная команда верна.

Я также пробовал с "-server-args='-screen 0 640x480x24'" (перевернутым " и ' , что привело к тому же результату ( Unrecognized option: '-screen )

Что происходит в subprocess.run этом изменении --server-args="-screen 0 640x480x24" ?

Комментарии:

1. --server-args="-screen 0 640x480x24" в bash точно эквивалентно "--server-args=-screen 0 640x480x24" . Кавычки — это синтаксис bash, вычисляемый посимвольно, а не данные для передачи xvfb-run , поэтому вам не следует заключать какие -либо литеральные кавычки в данные.

Ответ №1:

Правильный синтаксис был бы:

 args = [
    'xvfb-run',
    '--auto-servernum',
    '--server-args=-screen 0 640x480x24',
    '--error-file=/dev/stdout',
    '/opt/myExecutable'
]

try:
    from pipes import quote  # Python 2
except ImportError:
    from shlex import quote  # Python 3

command_str = ' '.join(quote(s) for s in args)
print(f'Command: {command_str}')

subprocess.run(args) # or subprocess.run(command_str, shell=True)
  

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

Комментарии:

1. OP переходит к объединению аргументов в одну строку, поэтому им все-таки нужны некоторые кавычки. Но, конечно, гораздо лучшим решением является просто сохранить аргументы в виде списка.

2. Они объединяются в строку для печати пользователю . Лучше использовать shlex.quote() для этой задачи для генерации содержимого в том же формате, который ожидается оболочкой.

3. @tripleee Я объединил в массив, чтобы посмотреть, какой будет команда для запуска. Я пропустил ту часть, где subprocess.run я бы привел свой аргумент, потому что в нем был пробел.

4. @Nepoxx, … то есть, когда вы запускаете xvfb-run --server-args="-screen 0 640x480x24" в bash, он становится массивом указателей на строки C, что также может быть описано как char**{"xvfb-run", "--server-args=-screen 0 640x480x24", NULL} — нигде ни в одной из этих строк нет кавычек; кавычки были использованы и удалены оболочкой, когда она приняла их за означающие, что 640x480x24 должно быть в том же аргументе, что и --server-args= .

5. @Nepoxx, «любое требуемое экранирование» оставляет открытой возможность того, что на некоторых платформах (то есть во всех системах, отличных от Windows) экранирование фактически не требуется. Смотрите man 2 execve руководство по базовому набору вызовов операционной системы.

Ответ №2:

Вот простой способ указать, как должна выглядеть команда, которую вы передаете subprocess.run . В оболочке (не Python, обычной оболочке) вставьте python -c 'import sys; print(sys.argv[1:])' перед командой, которую вы хотите запустить:

 19:59 ~ $ python -c 'import sys; print(sys.argv[1:])' xvfb-run --auto-servernum --server-args="-screen 0 640x480x24" --error-file=/dev/stdout /opt/myExecutable
['xvfb-run', '--auto-servernum', '--server-args=-screen 0 640x480x24', '--error-file=/dev/stdout', '/opt/myExecutable']
  

Результирующий список — это именно то, что вы должны передать subprocess.run . Здесь мы можем видеть, что оболочка преобразовала --server-args="-screen 0 640x480x24" во входных данных в один аргумент без кавычек.

Ответ №3:

Только не объединяйте команду в строку, а затем не ставьте кавычки там, где они были на месте, чтобы защитить строку от оболочки.

 args = ['xvfb-run', '--auto-servernum','--server-args=-screen 0 640x480x24', '--error-file=/dev/stdout', '/opt/myExecutable']
print(f'Command: {args}')
subprocess.run(args)