如何从一个子进程同步运行多个命令。Popen命令?

2024-05-23 14:32:09 发布

您现在位置:Python中文网/ 问答频道 /正文

是否可以使用相同的子进程命令按顺序执行任意数量的命令?

我需要每个命令在执行之前等待前一个命令完成,并且我需要它们都在同一个会话/shell中执行。我还需要在Python2.6和Python3.5中使用它。我还需要subprocess命令才能在Linux、Windows和macOS中工作(这就是为什么我在这里只使用echo命令作为示例)。

请参见下面的代码:

import sys
import subprocess

cmds = ['echo start', 'echo mid', 'echo end']

p = subprocess.Popen(cmd=tuple([item for item in cmds]),
                     stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

for line in iter(p.stdout.readline, b''):
    sys.stdout.flush()
    print(">>> " + line.rstrip())

如果这是不可能的,我应该采取什么方法来在同一个会话/shell中以同步顺序执行命令?


Tags: inimport命令echofor数量进程顺序
3条回答

如果要在同一个会话/shell中一个接一个地执行多个命令,则必须启动一个shell并将所有命令(一次一个,后跟一个新行)馈送给它,然后在最后关闭管道。如果有些命令不是真正的进程,而是可以改变shell环境的shell命令,这是有意义的。

在Windows下使用Python2.7的示例:

encoding = 'latin1'
p = subprocess.Popen('cmd.exe', stdin=subprocess.PIPE,
             stdout=subprocess.PIPE, stderr=subprocess.PIPE)
for cmd in cmds:
    p.stdin.write(cmd + "\n")
p.stdin.close()
print p.stdout.read()

要让这段代码在Linux下运行,您必须将cmd.exe替换为/bin/bash,并可能将编码更改为utf8。

对于Python 3,您必须对命令进行编码,可能还要对其输出进行解码,并在print中使用括号。

注意:这只会产生很小的效果。如果在关闭stdin管道之前有足够的输出来填充管道缓冲区,则此代码将死锁。一种更健壮的方法是让第二个线程读取命令的输出,以避免出现这种问题。

这与Serge Ballesta发布的答案相似,但并不完全相同。使用his进行异步执行,在异步执行中不关心结果。使用mine进行同步处理和结果收集。和他的回答一样,我在这里展示的是Windows解决方案-在Linux中运行bash进程,而不是在Windows中运行cmd。

from subprocess import Popen, PIPE
process = Popen( "cmd.exe", shell=False, universal_newlines=True,
                  stdin=PIPE, stdout=PIPE, stderr=PIPE )                             
out, err = process.communicate( commands ) 

用法详细信息:传递给commands方法的process.communicate参数是一个以新行分隔的字符串。例如,如果您只是将批处理文件内容读入字符串,则可以这样运行它,因为它已经有了换行符。

重要信息:字符串必须以换行结束"\n"。如果不这样做,最后的命令将无法执行。就像您在命令提示符中键入它,但没有在最后按enter。但是,您将在返回的stdout的末尾看到一个神秘的More?行。(这就是你遇到这种情况的原因)。

process.communicate按定义同步运行,并返回stdout和stderr消息(如果将它们定向到Popen构造函数中的subprocess.PIPE)。

当您以这种方式创建一个cmd.exe进程并向其传递一个字符串时,结果将与打开命令提示窗口并在其中输入命令完全相同。我是说真的。如果您对此进行测试,您将看到返回的stdout包含您的命令。(如果包含类似于执行批处理文件的@echo off,这并不重要)。

对那些关心“干净”标准输出结果的人的提示:

  • @echo off不会禁止您的命令出现在此返回的字符串中,但它确实会删除额外的换行符,否则这些换行符会出现在该字符串中。(universal_newlines=True将另一组数据剥离)

  • 在命令中包含一个@符号前缀允许它们仍然执行。在“正常”批处理过程中,逐行“隐藏”命令。在这种情况下,它是一个安全而简单的标记,通过它可以找到要删除的标准输出行。(如果有人这么倾向的话)

  • cmd.exe“header”将出现在您的输出中(显示Windows等的版本)。因为您可能想用@echo off来启动您的命令集,以删除额外的换行符,所以这也是一个找到标题行停止位置和命令/结果开始位置的好方法。

最后,为了解决“大”输出填充管道并给您带来问题的问题(首先,我认为您需要大量数据返回才能解决问题),这比大多数人在其用例中遇到的问题都要多。其次,如果真的有问题,只需打开一个文件进行写入,并将该文件句柄(对文件对象的引用)传递给stdout/err,而不是PIPE。然后,对创建的文件执行任何所需的操作。

一个可能的解决方案,看起来像是运行在同一个shell中:

subprocess.Popen('echo start;echo mid;echo end', shell=True)

注意-如果将命令作为字符串传递,那么shell必须为True 注意-这仅适用于linux,您可能需要在windows上找到类似的解决方法。

希望能有所帮助。

来自python文档-

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.

相关问题 更多 >