#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)