天天看点

一篇文章学会使用subprocess

作者:下辈子作神

subprocess 是 Python 中用于执行外部命令或程序的模块。它提供了一种在 Python 程序中启动和管理新进程的简便方法,可以通过该模块实现与 shell 的交互和管道操作。下面是使用 subprocess 模块的常用的参数介绍:

  1. args:要执行的命令,可以是一个列表或字符串。如果是一个字符串,将通过 shell 解析执行;如果是一个列表,则第一个元素是命令本身,后面的元素是命令的参数。

注意:在调用windows或Linux中的命令时,命令使用列表或者是字符串是分情况而定(本文会有举例介绍),若使用列表的形式,命令和参数分开写,例如:

['ping','-n','4','127.0.0.1']           

当使用下面的形式时,如果使用这种格式['ping -n 4 127.0.0.1']且shell参数指定为False时,程序执行会报错,原因是命令和参数混在一起,无法找到执行程序,这种写法和shell参数设置有关系。

process = subprocess.Popen(['ping 127.0.0.1'],shell=False, stdout=sb.PIPE)
FileNotFoundError: [WinError 2] 系统找不到指定的文件。
进程已结束,退出代码1           

注意:Popen的第一个参数复杂的拼接要注意一些问题。

subprocess.Popen 的第一个参数是一个列表或字符串,用于指定要执行的命令和参数。如果传入一个字符串,则会被解析为一个命令行,并在 shell 中执行。

例如,以下代码调用了 subprocess.Popen 启动了一个子进程并执行了 ls 命令:

import subprocess
p = subprocess.Popen(['ls', '-l'], stdout=subprocess.PIPE)           

在这个例子中,['ls', '-l'] 是一个列表,其中第一个元素是要执行的命令 ls,第二个元素是该命令的参数 -l。 stdout=subprocess.PIPE 表示将子进程的标准输出捕获到一个管道中,以便在父进程中获取子进程的输出内容。

如果将上面的代码改成以下形式:

p = subprocess.Popen('ls -l', stdout=subprocess.PIPE, shell=True)           

那么传入的第一个参数就是一个字符串,会在 shell 中执行。shell=True 表示在 shell 中执行命令,这样就可以使用 shell 中的一些特性,例如使用通配符或管道符号等。但是,在使用 shell=True 时,需要注意潜在的安全问题,因为这样可以执行任意的 shell 命令。

如何在第一个参数中传入指定路径?

要在 subprocess.Popen 的第一个参数中传入指定路径,需要将路径和命令及其参数分开。可以通过传入一个包含路径和命令的列表或字符串,或者传入一个字符串和 cwd 参数来指定路径。

例如,假设要在路径 /path/to/dir 下执行命令 ls -l,可以使用以下方式之一:

传入一个包含路径和命令的列表:

import subprocess
p = subprocess.Popen(['/path/to/dir/ls', '-l'], stdout=subprocess.PIPE)           

传入一个包含路径和命令的字符串:

import subprocess
p = subprocess.Popen('/path/to/dir/ls -l', stdout=subprocess.PIPE, shell=True)           

传入一个字符串和 cwd 参数来指定路径:

import subprocess
p = subprocess.Popen('ls -l', stdout=subprocess.PIPE, shell=True, cwd='/path/to/dir')           

cwd 参数指定了当前工作目录,ls 命令会在该目录下执行。注意,使用 shell=True 时,命令和参数需要用空格分隔,不需要用列表的形式传入。

  1. shell:是否通过 shell 执行命令。如果为 True,将通过 shell 解析命令并执行,可以执行一些 shell 内置的命令和管道操作。如果为 False,将直接执行命令而不经过 shell 解析。

内置命令调用遇到一些问题:

在执行dir命令时,dir命令是一个windows的内置命令,而不是一个独立的可执行程序。在shell=True的情况下,subprocess将通过shell来执行命令,shell将能够识别并执行内置命令。如果将shell参数设置为False,subprocess将尝试直接运行dir`命令的可执行文件,但是这个文件不存在,从而导致程序报错。

当shell参数设置为False时,subprocess将不会在Shell中运行命令,而是直接执行命令本身。这种方式更加安全,因为它不会解释任何Shell特性,也不会被Shell脚本所利用。因此,如果只需要简单地执行命令而不需要使用Shell特性,则应该将shell参数设置为False。否则,如果你需要使用Shell特性,则需要将shell参数设置为True。

  1. stdin:标准输入,可以是一个文件对象或一个字符串。如果是一个文件对象,则将其内容作为命令的标准输入。如果是一个字符串,则将其转换为字节流并作为标准输入。
  2. stdout:标准输出,可以是一个文件对象或一个整数。如果是一个文件对象,则将命令的标准输出写入该文件。如果是一个整数,则将其作为文件描述符,例如 subprocess.PIPE 将创建一个新的管道并将其作为标准输出。
  3. stderr:标准错误输出,可以是一个文件对象或一个整数。如果是一个文件对象,则将命令的标准错误输出写入该文件。如果是一个整数,则将其作为文件描述符,例如 subprocess.PIPE 将创建一个新的管道并将其作为标准错误输出。

subprocess.PIPE和subprocess.STDOUT` 都是常量。

在 subprocess.Popen 中,stdout 和 stderr 参数可以设置为以下三种值之一:

subprocess.PIPE 表示将标准输出或标准错误输出捕获到一个管道中。subprocess.STDOUT 表示将标准输出和标准错误输出合并为一个管道,并捕获到一个管道中。

None 表示将标准输出或标准错误输出直接输出到控制台,而不进行捕获。

需要注意的是,stdout 和 stderr 参数的类型都是 Any,即可以接受以上三种类型之一。

  1. cwd:指定命令执行的工作目录。
  2. env:指定执行命令时的。指定子进程的,可以是一个字典类型。。

若要在subprocess中传递,可以使用env参数。这个参数是一个字典,包含了要传递的和它们的值。例如:

env_vars = {'MY_VAR': 'my_value'}
subprocess.run(['echo', '$MY_VAR'], env=env_vars, shell=True)           

上例在子进程中执行一个echo命令,使用了$MY_VAR来引用。在env_vars字典中定义了MY_VAR的值为my_value,所以当子进程执行echo命令时,$MY_VAR会被替换为my_value。

import subprocess
# 设置
my_env = {'PATH': '/usr/local/bin'}
# 启动进程并传递
proc = subprocess.Popen(['ls'], env=my_env)
# 等待进程完成
proc.wait()           

在这个示例中,我们首先定义了一个名为 my_env 的字典,其中包含了一个 PATH 的值。然后,我们使用 subprocess.Popen 函数来启动一个新的进程,该进程运行 ls 命令,并通过 env 参数来传递我们定义的。最后,我们使用 proc.wait() 方法等待进程完成。

注意,如果没有传递 env 参数,子进程将继承父进程的。如果要修改子进程中的,可以在 env 字典中指定新的值。

  1. timeout:指定命令执行的超时时间,如果命令执行时间超过该时间,将引发 TimeoutExpired 异常。
  2. check:如果为 True,当命令执行返回值不为 0 时将引发 CalledProcessError 异常。
  3. text:如果为 True,则将命令的标准输出和标准错误输出解码为字符串。
  4. universal_newlines:是否将输出数据转换为文本模式,默认为 False。如果设置为 True,则会将输出数据转换为文本模式,即自动将 \r\n 转换为 \n。

除了上述参数外,还有一些其他的参数可以用于控制子进程的行为。例如:

12.start_new_session:是否将子进程放入新的进程组中,默认为 False。如果为 True,则可以使用 os.killpg() 来杀死子进程。

  1. creationflags`:指定创建进程的附加标志,例如 CREATE_NEW_CONSOLE 等。

需要根据实际需求来选择合适的参数,以达到控制子进程行为的目的。

以上是 subprocess 模块中常用的参数,使用这些参数可以满足大多数命令行操作的需求。需要注意的是,对于不熟悉的命令或者不信任的输入,最好不要使用 shell=True 参数,避免安全问题

使用案例

执行简单的命令-1:

以下是一个比较经典的使用 subprocess 模块的例子,它演示了如何在 Python 中调用一个外部命令并获取其输出:

# 执行 'dir' 命令,并获取输出
output = subprocess.check_output(['dir', '/w'],shell=True)

# 打印输出
print(output.decode("gbk"))           

在上面的代码中,使用 subprocess.check_output() 方法调用了内部命令dir ,并传递参数/w。check_output() 方法会等待命令执行完成,并返回命令的输出结果,以字节串的形式存储在 output 变量中。最后,将输出打印到屏幕上。

需要注意的是,check_output() 方法只能获取命令的标准输出,如果需要获取标准错误输出,需要使用其他方法,并结合 subprocess.PIPE 参数来获取输出结果。另外,如果命令执行失败,check_output() 方法会引发一个 subprocess.CalledProcessError 异常,需要在代码中进行异常处理。

check_output用在调用执行简单的命令时使用。

执行简单的命令-2:

subprocess.run(['ls', '-l'])           

这个例子使用 run 函数来执行 ls -l 命令,返回结果为 CompletedProcess 类型的对象,包含了命令的执行结果的返回码、标准输出和标准错误等,可以使用该对象来处理子进程的执行结果。

result = subprocess.run(['ls', '-l'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) 
print("返回码:", result.returncode) 
print("标准输出:", result.stdout.decode()) 
print("标准错误:", result.stderr.decode()) 
# 如果返回码为0,表示子进程执行           

执行简单的命令-3:捕获命令的输出:

result = subprocess.run(['ls', '-l'], capture_output=True)
print(result.stdout)           

使用 capture_output=True 参数可以捕获命令的标准输出和标准错误输出。

当设置capture_output参数为True,命令的标准输出被捕获并保存在一个名为stdout的成员变量中,标准错误输出的内容被保存在stderr成员变量中,以便在程序中进行处理。

如果capture_output参数为False(默认值),则命令的标准输出和标准错误输出将被传递到主进程的标准输出和标准错误输出中,就像在终端上运行命令时一样。

在上面的例子中,result.stdout 就是命令的标准输出。

执行简单的命令-4:传递命令参数:

filename = 'example.txt'
result = subprocess.run(['wc', '-l', filename], capture_output=True, text=True)
print(result.stdout)           

在这个例子中,我们使用 filename 变量来传递命令参数,使用 text=True 参数来将命令的输出解码为文本。

执行简单的命令-5:与 shell 交互:

windows下面的调用执行命令
result = subprocess.run("netstat.exe -an | findstr :135", shell=True, capture_output=True, text=True)
print(result.args)
print(result.stdout)

Linux系统下面的调用执行命令
result = subprocess.run("ss -antln | grep :22", shell=True)
print(result.args)
print(result.stdout)           

使用 shell=True 参数可以启动一个 shell 并执行命令,这样就可以进行更加复杂的管道操作。

案例1:使用subprocess的Popen调用exe程序,exe程序执行的时间不确定。

可以使用 subprocess.Popen 方法启动一个新的进程,并设置 stdout 参数为 subprocess.PIPE,这样子进程的标准输出会被捕获。然后可以使用 communicate() 方法等待子进程结束并获取标准输出数据。

示例代码:

# 启动进程
process = subprocess.Popen(['ping','-n','20','127.0.0.1'], stdout=subprocess.PIPE,shell=False)
while process.poll() is None:       #子进程还在执行
    output = process.stdout.readline().decode("gbk")
    if output:
        print(output.strip())

# 等待进程结束并获取返回值
return_code = process.wait()
print('Return code:', return_code)           

在上面的代码中,使用 readline() 方法逐行读取标准输出数据,如果有内容则输出内容,去掉字符串末尾的换行符,使用poll方法判断进程是否结束。

最后使用 wait() 方法等待进程结束,并获取进程的返回值。若返回值不为 0,则说明进程执行出错。

如果需要同时捕获标准输出和标准错误输出,可以将stdout和 stderr 参数也设置为 subprocess.PIPE,并使用 communicate() 方法获取标准输出和标准错误输出。

案例2:判断子进程是否结束

在 subprocess 模块中,poll() 方法是用于检查子进程是否已经结束的方法。如果子进程已经结束,poll() 方法将返回子进程的返回值,默认情况下返回0;否则将返回 None。

poll() 方法主要用于在子进程执行期间,定期检查子进程是否已经结束。通常情况下,可以在主进程中启动子进程,并使用 poll() 方法检查子进程的状态,以便在子进程结束后获取返回值。

以下是一个示例代码:

import time
process = subprocess.Popen(['ping','-n','20','127.0.0.1'])

# 检查进程状态
while process.poll() is None:
    print('The process is still running...')
    # 等待一段时间再次检查进程状态
    time.sleep(1)

# 获取返回值
return_code = process.poll()
print('The process has exited with return code:', return_code)           

在上面的代码中,使用 poll() 方法检查进程状态,如果返回值为 None,则说明进程仍在运行。使用 time.sleep() 方法等待一段时间后再次检查进程状态。当进程已经结束时,poll() 方法将返回进程的返回值,并跳出循环。

最后使用 poll() 方法获取进程的返回值。如果返回值不为 0,则说明进程执行出错。

案例3:使用subprocess调用exe程序,等待程序执行结束。

在 subprocess 模块中,可以使用 poll() 方法或 wait() 方法来检查子进程是否已经结束。

  • poll() 方法:检查子进程的状态,并返回子进程的返回值,如果子进程仍在运行,则返回 None。
  • wait() 方法:等待子进程结束,并返回子进程的返回值。

以下是一个示例代码,和上面代码区别的地方是,没有使用while循环,利用了子进程的wait方法。

import time
process = subprocess.Popen(['ping','-n','20','127.0.0.1'])

if process.poll() is None:
    print('The process is still running...')
else:
    return_code = process.poll()
    print('The process has exited with return code:', return_code)

# 等待进程结束
return_code = process.wait()
print('The process has exited with return code:', return_code)           

在上面的代码中,使用 poll() 方法检查进程状态,如果返回值为 None,则说明进程仍在运行;否则说明进程已经结束,使用 poll() 方法返回进程的返回值。

使用 wait() 方法等待进程结束,并获取进程的返回值。如果返回值不为 0,则说明进程执行出错。

需要注意的是,在使用 poll() 或 wait() 方法检查进程状态时,应该确保进程已经启动。如果进程还没有启动,这两个方法都将返回 None。可以使用 subprocess.Popen 方法启动进程,并将返回的进程对象保存下来,以便后续使用 poll() 或 wait() 方法检查进程状态。

案例4:

使用subprocess调用exe程序,获取程序结果,输出数据较少,并等待程序执行结束。

在 subprocess 模块中,subprocess.Popen 方法的 stderr 和 stdout 参数分别用于控制子进程的标准错误和标准输出流的处理方式。这两个参数的默认值都是 None,即未被设置,表示子进程的标准错误和标准输出流将被重定向到父进程的标准错误和标准输出流中。如果将这两个参数设置为 subprocess.PIPE,则表示子进程的标准错误和标准输出流将被重定向到管道中,父进程可以通过管道读取子进程的输出数据。

以下是一个示例代码:

# 将子进程的标准错误和标准输出流重定向到管道中
# 获取系统编码
code = locale.getpreferredencoding()
process = sb.Popen(['ping','-n','4','127.0.0.1'],stderr=sb.PIPE,stdout=sb.PIPE)
#读取子进程的标准输出流
output, error = process.communicate()
print('The output of the subprocess is:', output.decode(code))
if error:
    print('The error of the subprocess is:', error.decode(code))           

在上面的代码中,使用 subprocess.PIPE 将子进程的标准输出流和标准错误流重定向到管道中,并使用 process.communicate() 方法读取子进程的输出数据。使用 decode() 方法将二进制数据转换为字符串数据。

注意的是,如果不对子进程的输出进行适当的处理,可能会导致子进程阻塞或者出现缓冲区溢出等问题。如果子进程输出的数据量很大,可以使用 process.stdout.read() 或 process.stderr.read() 方法直接读取数据;如果子进程的输出数据量很小,可以使用 subprocess.communicate() 方法读取子进程的输出数据。在读取数据时,需要注意子进程的输出流是否已经关闭,否则可能会阻塞读取。

案例5:调用GUI程序时,显示/隐藏窗口。

在 subprocess 模块中,Popen() 方法的 creationflags 参数可以用于指定创建进程时的附加标志。这些标志可以用于控制进程的行为,更多的是控制GUI界面的进程启动后的显示、隐藏,例如:

  • CREATE_NEW_CONSOLE:创建一个新的控制台窗口,可以用于在控制台中查看子进程的输出。
  • DETACHED_PROCESS:将子进程分离到新的进程组中,子进程不会收到 Ctrl-C 等信号。
  • CREATE_NEW_PROCESS_GROUP:创建一个新的进程组,可以用于将子进程与父进程分开。

需要注意的是,使用这些标志时需要谨慎,因为它们可能会影响进程的行为,导致意外的结果。例如,如果使用 CREATE_NEW_CONSOLE 标志创建一个新的控制台窗口,则可能会出现多个控制台窗口同时打开的情况,这可能会对用户造成困扰。

在选择使用附加标志时,需要根据实际需求进行评估,并且需要对可能导致的风险有足够的认识。同时,需要注意,某些附加标志可能只在特定的操作系统或平台上受支持,因此需要查看相应的文档或者测试代码以确保其可用性。

注:在调用执行shell指令或非GUI程序时,进程创建参数不会起作用。

如果使用下面的代码,在windows系统将会出现ping的进程。

process = sb.Popen(['ping','-n','4','127.0.0.1'],stderr=sb.PIPE,stdout=sb.PIPE)
与下面的代码
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = 0
process = sb.Popen(['ping','-n','4','127.0.0.1'],stderr=sb.PIPE,stdout=sb.PIPE,startupinfo=startupinfo)
执行完全一致;           

类似下图所示:

案例6:调用一个带有GUI界面exe程序,窗口最大化;

startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = 3
print(startupinfo.wShowWindow)
subprocess.run(['notepad'], startupinfo=startupinfo)           

startupinfo.wShowWindow的值是ShowWindow利用的Windows API中的一个函数,它用于显示或隐藏一个窗口。它的API定义如下:

BOOL ShowWindow(
  HWND hWnd,
  int nCmdShow
);           

startupinfo.wShowWindow它可以是以下值之一;

- `SW_HIDE`(0):隐藏窗口。
- `SW_SHOWNORMAL`(1):用正常大小和位置显示窗口。如果窗口最小化或最大化,则恢复为原来的大小和位置。
- `SW_SHOWMINIMIZED`(2):最小化窗口,激活下一个顶层窗口。
- `SW_SHOWMAXIMIZED`(3):最大化窗口。
- `SW_SHOWNOACTIVATE`(4):用正常大小显示窗口,但不激活窗口。不要将这个标志与`SW_SHOW`或`SW_SHOWNORMAL`一起使用。
- `SW_SHOW`(5):激活并用正常大小显示窗口。
- `SW_MINIMIZE`(6):最小化窗口。
- `SW_SHOWMINNOACTIVE`(7):最小化窗口,不激活窗口。
- `SW_SHOWNA`(8):用当前大小和位置显示窗口,不激活窗口。
- `SW_RESTORE`(9):恢复窗口的大小和位置,如果窗口最小化或最大化,则恢复为原来的大小和位置。
- `SW_SHOWDEFAULT`(10):根据在STARTUPINFO结构体中指定的SW_标志显示窗口。如果未指定SW_标志,则使用SW_SHOWNORMAL。           

案例7:

调用命令,将结果输出到指定文件;

import subprocess

# 创建一个STARTUPINFO结构体对象并设置一些属性
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = subprocess.SW_HIDE

# 执行一个新进程并将stdout和stderr重定向到文件
with open('output.txt', 'w') as f:
    subprocess.run(['ls', '-la'], stdout=f, stderr=f, startupinfo=startupinfo)
           

在上面的代码中,startupinfo 参数被设置为 subprocess.STARTUPINFO() 对象,其中 dwFlags 属性被设置为 subprocess.STARTF_USESHOWWINDOW 标志,表示使用 dwFlags 来控制子进程窗口的显示。然后,在调用 Popen() 方法时,将 startupinfo 参数传递给该方法,并将 creationflags 参数设置为 subprocess.CREATE_NO_WINDOW 标志,表示创建一个没有窗口的子进程。

需要注意的是,CREATE_NO_WINDOW 标志只在 Windows 操作系统上受支持,如果在其他操作系统上使用该标志可能会导致错误。如果需要在其他操作系统上隐藏控制台窗口,可以使用其他方法,例如将标准输出和标准错误流重定向到文件中,或者使用 GUI 应用程序来启动 exe 程序。

调用程序输出内容时候,指定编码。在使用 subprocess 模块时,可以使用 encoding 参数指定输出的编码格式,以便正确地解码输出数据。具体做法是:

proc = subprocess.Popen(['your_command'], stdout=subprocess.PIPE, encoding='utf-8')
output, error = proc.communicate()           

在上面的代码中,将 encoding 参数设置为 'utf-8',表示输出数据是以 UTF-8 编码格式编码的。当您从子进程的标准输出或标准错误流中读取数据时,Python 将使用指定的编码格式对数据进行解码,并将其转换为字符串。请注意,encoding 参数仅适用于 Python 3,如果您正在使用 Python 2,请使用 subprocess 模块的 codecs 模块或其他相关模块来处理输出数据的编码。

需要注意的是,如果子进程的输出数据是以其他编码格式编码的,那么需要根据实际情况来设置 encoding 参数,在python3中使用locale库获取系统的编码。如果无法确定输出数据的编码格式,可以尝试使用 chardet 等库来自动检测编码格式。

import locale

code = locale.getpreferredencoding()
with open('output.txt', 'w') as f:
    sb=subprocess.Popen(['ping', '-n','4','127.0.0.1'],  stdout=f, stderr=f, encoding=code)
    sb.communicate()           

案例8:

subprocess中执行调用命令,并传递环境变量

subprocess 中,可以使用 os.environ 字典来设置,然后将其传递给 subprocess.Popen() 方法。具体步骤如下:

import os
env=os.environ.copy() #使用本机的资源
result = subprocess.run("java --version", shell=True, capture_output=True, text=True,env=env)
print(result.args)
print(result.stdout)

============================================================
env = os.environ.copy()
env['MY_VAR'] = 'my_value'

# 执行 .bat 文件
bat_file = 'my_script.bat'
process = subprocess.Popen(bat_file, env=env)           

在上面的代码中,首先使用 os.environ.copy() 方法创建了一个包含所有当前的字典,然后将 MY_VAR 设置为 my_value。接下来,将字典传递给 subprocess.Popen() 方法,以便在执行 .bat 文件时传递这些变量。

需要注意的是,在 .bat 文件中,可以通过 %MY_VAR% 的形式来引用 MY_VAR 。这样,在执行 .bat 文件时,子进程就可以使用这个了。

案例9:调用程序,持续读取输出。

#coding="utf-8"
import subprocess
import select
process = subprocess.Popen(['ping', '-c', '10000', '127.0.0.1'],shell=False,
                           stdout=subprocess.PIPE,stderr=subprocess.PIPE)

while True:
    readable, _, _ = select.select([process.stdout], [process.stderr], [])
    if process.stdout in readable:
        output = process.stdout.readline()
        if output:
            print(output.strip())
    if process.stderr in readable:
        err_output = process.stderr.readline()
        if err_output:
            print(err_output.strip())
    else:
        if process.poll() is not None:
            break

return_code = process.wait()
print('Return code:', return_code)           

注意:上述代码在windows上无法执行,原因是windows系统对于python的select语句支持不够全面。

在 Windows 上,select模块仅适用于套接字;

案例10:调用Powershell执行脚本

#coding=“utf-8”

import subprocess as sp

def exec_powershell(powershellPath,PSfullpath):
    try:
        args = [powershellPath, r" -ExecutionPolicy", r" Unrestricted",PSfullpath]
        cmd=" ".join(args)
        p = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.PIPE, shell=True)
        stdout=p.stdout.read().decode("gbk")
        stderr=p.stderr.read().decode("gbk")
        print(stderr,stdout)

    except Exception as e:
        print(e)
    return False

if __name__=="__main__":
    exec_powershell(r'C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe',r'g:\query1.PS1')           

请编写一个调用exe程序,并不断获取其输出的过程,需要使用poll检查进程是否结束,一旦子进程结束程序就全部结束

案例11:调用exe 程序,并不断获取其输出的例子,同时使用 poll() 方法检查进程是否结束,如果子进程结束,程序就会停止输出并退出:

import subprocess

# 执行命令并获取输出
proc =  subprocess.Popen(['ping', '-c', '10000', '127.0.0.1'],shell=False,
                           stdout=subprocess.PIPE,stderr=subprocess.PIPE)

while proc.poll() is None:
    # 持续获取输出
    output = proc.stdout.readline()
    if output:
        print(output.decode('utf-8').strip())

# 获取剩余的输出
for output in proc.stdout.readlines():
    print(output.decode('utf-8').strip())

# 等待子进程结束
proc.wait()           

subprocess.Popen.poll() 方法返回子进程的返回值,如果子进程尚未结束,它会立即返回 None。如果子进程已经结束,它会返回子进程的返回值。

如果需要等待子进程结束并获取其返回值,可以使用 subprocess.Popen.wait() 方法。该方法会阻塞当前进程直到子进程结束,并返回子进程的返回值。另外,subprocess.Popen.communicate() 方法也可以等待子进程结束并获取其标准输出和标准错误输出。

使用 wait() 方法等待子进程结束并获取其返回值:

import subprocess

p = subprocess.Popen(['echo', 'hello world'], stdout=subprocess.PIPE)
returncode = p.wait()  # 等待子进程结束并获取返回值
output = p.stdout.read().decode('utf-8').strip()  # 获取子进程的标准输出
print(f'Return code: {returncode}')
print(f'Output: {output}')           

注意,如果子进程出现了异常,wait() 方法会抛出异常,可以使用 try-except 语句进行捕获和处理。

案例11:调用exe,并通过标准输入结束程序。

在使用subprocess模块时,可以使用stdin参数向子进程发送输入数据。以下是使用subprocess模块向子进程发送输入的示例代码:

import subprocess

# 启动子进程
p = subprocess.Popen(['my_program', 'arg1', 'arg2'], stdin=subprocess.PIPE)

# 向子进程发送输入
input_data = "input string"
p.stdin.write(input_data.encode())
p.stdin.close()

# 等待子进程结束并获取输出
output, errors = p.communicate()           

在这个例子中,Popen()函数的stdin参数被设置为subprocess.PIPE,这意味着subprocess会创建一个管道,允许主进程向子进程发送输入。然后,我们使用p.stdin.write()方法将输入数据发送到子进程的标准输入流。最后,我们使用p.communicate()方法等待子进程结束并获取输出结果。

需要注意的是,stdin.write()方法将输入数据作为字节串发送,因此需要使用encode()方法将输入字符串转换为字节串。另外,当使用stdin参数向子进程发送输入时,应该在完成输入之后显式关闭标准输入流(使用stdin.close()方法),以便让子进程知道输入已经结束。

上述介绍的subprocess程序调用方法基本上能覆盖日常运维及工作中,如果有好的经验和技巧可私信给我。

继续阅读