将列表传递给subprocess.run

5

我有一个包含列表的脚本,这个列表只是一些参数,我想将它们传递给subprocess.run,就像这样:

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

这是我的代码。
commands = ["bash command 1", "bash command 2",..]
process = subprocess.run([commands], stdout = subprocess.PIPE, shell = True)

我应该如何将列表传递给我的subprocess.run?

这是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

我不知道我做错了什么,而且我已经尝试了各种方法,所以我非常感激任何帮助。


抱歉,那其实是我在输入问题时犯的错误,我已经编辑了我的问题。 - skdadle
2
只需从“.run([commands]”中删除“[]”。 - MisterMiyagi
@Rakesh 感谢您的反馈,我已经按照您的建议更改了代码,现在没有出现任何错误,但是似乎只传递了第一个参数给bash,而忽略了其余的参数。我可能做错了什么?非常感谢! - skdadle
1
@skdadle 不要使用 shell=True。它会改变命令列表的解释方式。 - Giacomo Alzetta
好的,当我使用list(commands)时它可以工作,也可以在去掉[ ]时工作,我在那里得到了相同的结果,它只是将我的列表中的第一个字符串传递给bash。它们是单独的命令,希望我没有让你困惑。 @MisterMiyagi 我的参数基本上是"cd ~/app cli && bash app",其中包含指定用户名和密码以运行该应用程序的参数,但我将它们全部放在一个字符串中,如下所示"cd ~/app cli && bash app -- email some-email --password somepassword .. " - skdadle
显示剩余3条评论
1个回答

10

在使用shell=True之前,你必须了解它的作用。请参考Popen的文档。文档中指出:

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.

没有一种方法可以一次性执行一系列命令,你所做的是执行第一个命令,并将所有其他命令作为传递给该shell的选项一起执行。

相反,你应该这样做:

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

或者:您希望将一系列命令作为单个脚本执行:

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

这段话的意思是:如果command内的命令只是可执行文件,你应该避免使用shell=True,并使用shlex.split来提供已解析的参数列表。如果你需要指定命令应该执行的目录,你可以使用cwd参数来调用Popen(或任何类似的函数)。
在您的情况下,您需要像这样做:
import os, subprocess

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

谢谢你的回答!但是现在我的列表中的第一个字符串出现了无效语法错误,但是当我像这样正常地将它作为一个参数传递时,例如:subprocess.run("与我的列表中第一个字符串完全相同的字符串", shell=true),它可以正常工作。有什么想法吗? - skdadle
在您的回答中,您向我展示了如何传递一个参数给subprocess.run,我是否可以传递一个序列/列表的参数,让它们依次执行? - skdadle
@skdadle subprocess.run总是执行一件事情。 这件事可以是一个可执行文件(当shell=False时),此时参数是指定命令行的字符串列表; 或者它可以执行单个shell脚本(当shell=True时)。 shell脚本可以由多个shell命令组成。 在UNIX风格的shell中,您可以使用;(或换行符)分隔多个命令。 如我所说:如果可能的话,请避免使用shell=True或执行多个命令。 如果您有更多问题,请编辑您的问题以包括您收到的错误。 - Giacomo Alzetta
嗨,感谢你的回答。我尝试了你的解决方案-使用 (';'.join(commands), shell = True) 但是我得到了一个无效语法错误。我感到困惑,因为每个命令都用双引号括起来,由逗号分隔,并且我认为我们应该像在 shell 中编写一样准确地编写我们的命令。我不能使用你建议的第二个答案,因为我有更多的命令,而且我不想单独键入每个命令,这也是我之前想使用列表的原因。此外,我需要 shell=True,因为我没有运行可执行文件。非常感谢! - skdadle
@skdadle 如果您不提供要运行的确切命令列表,我们就无法知道为什么会出现语法错误。如果您想获得更好的答案,应尽可能在问题中提供更多信息。如果您提供通用信息,则会收到通用答案。有时这已经足够了,但有时它们是次优的。 - Giacomo Alzetta

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接