user924069
user924069

Reputation:

Python subprocess call can't find full path on Mac OS X

In my Python script, this line:

call("/Applications/BitRock\\ InstallBuilder\\ for\\ Qt\\ 8.5.2/bin/Builder.app/Contents/MacOS/installbuilder.sh")

will fail every time with the error OSError: [Errno 2] No such file or directory

However, if I write out the result of that string:

sys.stdout.write("/Applications/BitRock\\ InstallBuilder\\ for\\ Qt\\ 8.5.2/bin/Builder.app/Contents/MacOS/installbuilder.sh")

I get:

/Applications/BitRock\ InstallBuilder\ for\ Qt\ 8.5.2/bin/Builder.app/Contents/MacOS/installbuilder.sh

If I put that directly into the terminal, it works perfect.

What am I missing?

Upvotes: 0

Views: 8723

Answers (1)

Johannes Weiss
Johannes Weiss

Reputation: 54111

By default subprocess.call uses no shell (shell=False). Therefore, there's no need to escape the spaces. The space escaping is needed in shells (because shells need to know what's the binary's name and what the arguments). Therefore the following uses are all correct (and similar):

  1. Without spawning a shell that spawns the subprocess (favorable):

    from subprocess import call
    call('/Applications/BitRock InstallBuilder for Qt 8.5.2/bin/Builder.app/Contents/MacOS/installbuilder.sh')
    
  2. or again without shell (explicit shell=False and the usage of an argument list)

    from subprocess import call
    call(['/Applications/BitRock InstallBuilder for Qt 8.5.2/bin/Builder.app/Contents/MacOS/installbuilder.sh'],
         shell=False)
    
  3. But when subprocess is told to first of all swawn shell which then spawns the subprocess itself, the spaces have to be escaped, because it's a shell command:

    from subprocess import call
    call('/Applications/BitRock\\ InstallBuilder\\ for\\ Qt 8.5.2/bin/Builder.app/Contents/MacOS/installbuilder.sh',
         shell=True)
    
  4. An alternative would be to use a shell and quotes:

    from subprocess import call
    call('"/Applications/BitRock InstallBuilder for Qt 8.5.2/bin/Builder.app/Contents/MacOS/installbuilder.sh"',
         shell=True)
    

I'd recommend not to use a shell whenever possible (mostly for security reasons), remember though you have to pass the arguments to the command as a list if you're not using a shell.

Either (without shell, favourable):

call(['/bin/echo', 'foo', 'bar'])

or with a shell

call('/bin/echo foo bar', shell=True)

(both calls have the same output (foo bar\n)

Upvotes: 7

Related Questions