skdadle
skdadle

Reputation: 165

Passing List to subprocess.run

i've got a script, that contains a list - the list is just some args that I want to pass to subprocess.run like this

commands = ["bash command 1", "bash command 2",..]

here is my code

commands = ["bash command 1", "bash command 2",..]
process = subprocess.run([commands], stdout = subprocess.PIPE, shell = True)

how can I pass a list to my subprocess.run?

this is the Traceback

Traceback (most recent call last):
  File "./retesting.py", line 18, in <module>
    process = subprocess.run([commands], stdout = subprocess.PIPE, shell = True)
  File "/usr/lib/python3.5/subprocess.py", line 383, in run
    with Popen(*popenargs, **kwargs) as process:
  File "/usr/lib/python3.5/subprocess.py", line 676, in __init__
    restore_signals, start_new_session)
  File "/usr/lib/python3.5/subprocess.py", line 1221, in _execute_child
    restore_signals, start_new_session, preexec_fn)
TypeError: Can't convert 'list' object to str implicitly

I Have no idea what I'm doing wrong and I tried all sorts of things, so I'd appreciate really any help

Upvotes: 5

Views: 15993

Answers (1)

Giacomo Alzetta
Giacomo Alzetta

Reputation: 2479

Before using shell=True you have to understand what it does. Take the documentation for Popen. It states:

The shell argument (which defaults to False) specifies whether to use the shell as the program to execute. If shell is True, it is recommended to pass args as a string rather than as a sequence.

On Unix with shell=True, the shell defaults to /bin/sh. If args is a string, the string specifies the command to execute through the shell. This means that the string must be formatted exactly as it would be when typed at the shell prompt. This includes, for example, quoting or backslash escaping filenames with spaces in them. If args is a sequence, the first item specifies the command string, and any additional items will be treated as additional arguments to the shell itself. That is to say, Popen does the equivalent of:

Popen(['/bin/sh', '-c', args[0], args[1], ...])

On Windows with shell=True, the COMSPEC environment variable specifies the default shell. The only time you need to specify shell=True on Windows is when the command you wish to execute is built into the shell (e.g. dir or copy). You do not need shell=True to run a batch file or console-based executable.

There is no way to execute a sequence of commands in one go, what you are doing is executing the first command and all other commands as passed when spawning the shell as options to that shell.

You want to do this instead:

for command in commands:
    subprocess.run(command, shell=True)

Alternatively: you want to execute the sequence of commands as a single script:

subprocess.run(';'.join(commands), shell=True)

This said: if the commands inside command are only executables you should really avoid using shell=True and use shlex.split to provide the parsed argument list. If you need to specify the directory where the command should be executed you can use the cwd argument to Popen (or any similar function).

In your case you want something like:

import os, subprocess

subprocess.run(['app', '--email', 'some-email', '--password', 'somepassword'], cwd=os.path.expanduser('~/app-cli'))

Upvotes: 10

Related Questions