Nepoxx
Nepoxx

Reputation: 5358

Using subprocess.run with arguments containing quotes

The command I'm trying to run looks like this:

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

This is what I have in 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)

I get the following:

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

The printed command is correct.

I also tried with "-server-args='-screen 0 640x480x24'" (inverted " and ' which led to the same result (Unrecognized option: '-screen)

What goes on in subprocess.run that changes --server-args="-screen 0 640x480x24"?

Upvotes: 6

Views: 2954

Answers (3)

user2357112
user2357112

Reputation: 282026

Here's a simple way to tell what the command you pass to subprocess.run should look like. In the shell (not Python, a regular shell), insert python -c 'import sys; print(sys.argv[1:])' before the command you want to run:

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']

The resulting list is exactly what you should pass to subprocess.run. Here, we can see that the shell converted the --server-args="-screen 0 640x480x24" in the input into a single argument with no quotes in it.

Upvotes: 5

Charles Duffy
Charles Duffy

Reputation: 295934

Correct syntax would be:

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)

Note that there are no literal quotes here at all -- the only quotes are Python syntax. In bash, unescaped quotes are syntax, not data, even when they exist part of the way through a string.

Upvotes: 5

tripleee
tripleee

Reputation: 189936

Just don't merge the command into a string, and then don't put quotes where they were in place to protect a string from the shell.

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

Upvotes: 4

Related Questions