Technext
Technext

Reputation: 8107

Passing command with args to subprocess.Popen

Following script works fine when I try passing a batch file (that doesn't require any arguments) to the function.

script_as_var = os.path.join(os.environ['SOME_VAR'], 'bin/stop.bat')
jboss = os.path.join(os.environ['JBOSS_HOME'], 'bin/jboss-cli.bat')

def status(some_arg, cmd, more_arg):
    print('CMD: ' + cmd)
    ps = subprocess.Popen(cmd, stdout=subprocess.PIPE)
    output = ps.communicate()[0]
    ...
    ...

status(10, script_as_var, 'last_arg')   # Works fine

However, when I try passing below command to the function, it fails.

status(15, '[jboss,'"'-c'"','"'--commands="'"read-attribute server-state"'"'"']',
       'some_arg')

Output:

CMD: [jboss,'-c','--commands='"read-attribute server-state"'']
...
FileNotFoundError: [WinError 2] The system cannot find the file specified

Below command used to work fine when I didn't create a function.

ps = subprocess.Popen([jboss,'-c','--commands='"read-attribute server-state"''],
                      stdout=subprocess.PIPE)

As you can notice from the output of print command, I was successfully able to somehow (by using the messy single and double quotes combination) pass the exact command that I was able to run when this function was not created but it seems it sill needs some work for jboss variable to expand.

Any idea how I can fix this?

Upvotes: 0

Views: 928

Answers (2)

martineau
martineau

Reputation: 123413

The subprocess.Popen constructor will accept either a string or a sequence like a list as its first argument named args. You code appears to be trying to use both...and here's how to do that correctly. As you can see, it's also expanding the jboss variable as desired.

import os
os.environ['SOME_VAR'] = 'some_var'          # For testing
os.environ['JBOSS_HOME'] = 'jboss_home_var'  # For testing

script_as_var = os.path.join(os.environ['SOME_VAR'], 'bin/stop.bat')
jboss = os.path.join(os.environ['JBOSS_HOME'], 'bin/jboss-cli.bat')

def status(some_arg, cmd, more_arg):
    print('CMD: ' + repr(cmd))
#    ps = subprocess.Popen(cmd, stdout=subprocess.PIPE)
#    output = ps.communicate()[0]
#    ...
#    ...

status(10, script_as_var, 'last_arg')
status(15, [jboss, '-c', '--commands="read-attribute server-state"'], 'some_arg')

Output:

CMD: 'some_var\\bin/stop.bat'
CMD: ['jboss_home_var\\bin/jboss-cli.bat', '-c', '--commands="read-attribute server-state"']

Upvotes: 2

Scott
Scott

Reputation: 1296

You're passing a str for command. Note that your working example has a list. You just need to get rid of all the extra quoting and pass a list.

>>> def foo(a, b, c):
  print(b) 

>>> jboss = 42

>>> foo(15,'[jboss,'"'-c'"','"'--commands="'"read-attribute server-state"'"'"']','some_arg')
[jboss,'-c','--commands="read-attribute server-state"']

>>> foo(15,[jboss,'-c','--commands="read-attribute server-state"'],'some_arg')
[42, '-c', '--commands="read-attribute server-state"']

Upvotes: 1

Related Questions