python常用库学习四——subprocess

目录

前言

1、subprocess.run()

(1)函数参数列表

(2)函数的参数含义

(3)使用示例

2、subprocess.Popen

(1)函数参数列表

(2)函数参数含义

(3)使用示例

3、常见的subprocess.Popen类方法

(1)Popen.poll()

(2)Popen.communicate(input=None, timeout=None)

(3)Popen.send_signal(signal)

(4)Popen.terminate()

(5)Popen.kill()

(6)Popen.stdout

(7)Popen.stderr

(8)Popen.pid

(9)Popen.returncode

4、subprocess.check_call

(1)函数参数列表

(2)参数详解

(3)使用示例

5、subprocess.check_output

(1)函数参数列表

(2)参数详解

(3)使用示例


前言

Python的`subprocess`库主要用于启动新的应用程序或进程,并与它们的输入/输出/错误管道进行交互。

以下是`subprocess`库的一些主要用途:

1. 运行外部命令:你可以使用`subprocess`库运行任何在命令行下可以运行的命令或程序。

2. 交互式进程通信:你可以使用`subprocess`库启动一个新的进程,并通过其标准输入、输出和错误管道与之交互。比如,你可以发送数据给进程的标准输入,或者读取进程的标准输出和错误输出。

3. 管道和重定向:你可以使用`subprocess`库创建管道,将一个进程的输出重定向到另一个进程的输入,或者将进程的输出重定向到一个文件。

4. 进程管理:你可以使用`subprocess`库监控进程的状态,等待进程结束,或者终止进程。

`subprocess`库提供了一套高级API(如`run()`,`check_output()`等函数)和一套低级API(如`Popen`类)。高级API简单易用,适合于简单的用途,如运行一个命令并获取其输出。低级API功能强大,灵活性高,适合于复杂的用途,如交互式进程通信和复杂的管道和重定向。

1、subprocess.run()

(1)函数参数列表

subprocess.run(args*stdin=Noneinput=Nonestdout=Nonestderr=Nonecapture_output=Falseshell=Falsecwd=Nonetimeout=Nonecheck=Falseencoding=Noneerrors=Nonetext=Noneenv=Noneuniversal_newlines=None**other_popen_kwargs)

(2)函数的参数含义

1. `args`:这是要执行的命令和参数,可以是一个字符串或一个参数列表。如果是一个字符串,它会被传递给shell执行;如果是一个参数列表,列表的第一个元素是要执行的命令,后面的元素是命令的参数。

2. `stdin`:这是子进程的标准输入。如果设为`subprocess.PIPE`,你可以通过`input`参数向子进程发送数据;如果设为`subprocess.DEVNULL`,表示关闭子进程的标准输入。

3. `input`:这是发送给子进程的数据。它应该是一个字节串或一个字符串。

4. `stdout`、`stderr`:这两个参数分别表示子进程的标准输出和标准错误。你可以设为`subprocess.PIPE`来捕获子进程的输出/错误,或者设为`subprocess.DEVNULL`来忽略子进程的输出/错误。

5. `capture_output`:如果设为`True`,表示捕获子进程的标准输出和标准错误。

6. `shell`:如果设为`True`,表示在一个shell中执行命令。这在执行需要shell特性(比如管道、文件重定向等)的命令时非常有用。

7. `cwd`:这是子进程的工作目录。默认为None,表示使用当前进程的工作目录。

8. `timeout`:这是子进程的超时时间(以秒为单位)。如果子进程在超时时间内未完成,将引发`TimeoutExpired`异常。

9. `check`:如果设为`True`,表示当子进程返回非零退出状态时,引发`CalledProcessError`异常。

10. `encoding`、`errors`:这两个参数用于编码/解码子进程的输入/输出。默认为None,表示使用系统默认编码。

11. `text`:如果设为`True`,表示以文本模式处理输入/输出(默认为字节模式)。

12. `env`:这是子进程的环境变量。默认为None,表示使用当前进程的环境变量。

13. `universal_newlines`:这个参数在Python 3.7及更高版本中被`text`参数取代。

14. `**other_popen_kwargs`:这是其他传递给`subprocess.Popen()`的关键字参数。

(3)使用示例

- 运行一个简单的命令:

import subprocess

subprocess.run('ls -l', shell=True)

- 捕获子进程的输出:

result = subprocess.run('ls -l', shell=True, stdout=subprocess.PIPE, text=True)
print(result.stdout)

- 向子进程发送数据:

result = subprocess.run('cat', shell=True, stdout=subprocess.PIPE, 
                        stdin=subprocess.PIPE, input='hello, world!', text=True)
print(result.stdout)

- 处理子进程的非零退出状态:

try:
    subprocess.run('false', shell=True, check=True)
except subprocess.CalledProcessError:
    print('Command failed')

2、subprocess.Popen

(1)函数参数列表

subprocess.Popen(argsbufsize=- 1executable=Nonestdin=Nonestdout=Nonestderr=Nonepreexec_fn=Noneclose_fds=Trueshell=Falsecwd=Noneenv=Noneuniversal_newlines=Nonestartupinfo=Nonecreationflags=0restore_signals=Truestart_new_session=Falsepass_fds=()*group=Noneextra_groups=Noneuser=Noneumask=- 1encoding=Noneerrors=Nonetext=Nonepipesize=- 1process_group=None)

(2)函数参数含义

1、`args`:要执行的命令和参数。如果`shell=False`(默认),`args`应该是一个序列,比如`['ls', '-l']`;如果`shell=True`,`args`可以是一个字符串,比如`'ls -l'`。
2、 `bufsize`:指定缓冲策略。0表示不缓冲,1表示行缓冲,其他正数表示使用一个近似大小的缓冲区,负数表示使用系统默认的IO缓冲策略。
3、 `executable`:实际程序路径,一般不需要指定,由`args[0]`确定。
4、 `stdin`, `stdout`, `stderr`:分别表示子进程的标准输入、标准输出和标准错误。可以是任何`file-like`对象,也可以是`subprocess.PIPE`(创建新的管道)、`subprocess.STDOUT`(将错误输出到标准输出流中)或`subprocess.DEVNULL`(忽略输出)。
5、`preexec_fn`:仅在Unix平台上有效,用于指定一个可调用对象,它将在子进程运行之前被调用。
6、`close_fds`:在Unix平台上,如果`close_fds`为`True`,则除了0, 1和2之外的所有文件描述符将在子进程启动之前关闭。在Windows上,如果`close_fds`为`True`,则新创建的子进程将不会继承父进程的输入、输出和错误流。默认为`True`。
7、`shell`:如果`shell=True`,则在一个shell中执行命令。这对于需要执行shell命令的情况非常有用,比如使用管道、通配符等。
8、 `cwd`:如果被指定,子进程的当前工作目录将被改变为`cwd`。
9、 `env`:一个字典,定义新进程的环境变量。
10、`universal_newlines`:如果`True`,则在此进程的输入/输出中使用通用换行符。
11、`startupinfo`和`creationflags`只在Windows上有效。
12、 `restore_signals`, `start_new_session`和`pass_fds`只在Unix上有效。

(3)使用示例

1、运行命令并获取输出

import subprocess

# 创建Popen对象
proc = subprocess.Popen(['ls', '-l'], stdout=subprocess.PIPE)

# 获取命令的输出
out, err = proc.communicate()

# 打印命令的输出
print(out.decode('utf-8'))

2、使用shell方式运行命令

import subprocess

# 创建Popen对象
proc = subprocess.Popen('ls -l | grep .txt', shell=True, stdout=subprocess.PIPE)

# 获取命令的输出
out, err = proc.communicate()

# 打印命令的输出
print(out.decode('utf-8'))

3、使用环境变量:

import subprocess
import os

# 创建环境变量字典
env = os.environ.copy()
env['NEW_ENV_VAR'] = 'value'

# 创建Popen对象
proc = subprocess.Popen(['printenv', 'NEW_ENV_VAR'], env=env, stdout=subprocess.PIPE)

# 获取命令的输出
out, err = proc.communicate()

# 打印命令的输出
print(out.decode('utf-8'))

3、常见的subprocess.Popen类方法

(1)Popen.poll()

`Popen.poll()`方法用于检查子进程(由Popen实例代表)是否已经结束。

- 如果子进程已经结束,`poll()`方法将返回子进程的返回码。
- 如果子进程还在运行,`poll()`方法将返回`None`。

因此,你可以通过调用`poll()`方法来判断子进程是否还在运行。

这是一个使用示例:

import subprocess
import time

# 启动一个子进程
p = subprocess.Popen(['sleep', '5'])

# 每秒检查一次子进程的状态
while p.poll() is None:
    print('Still running...')
    time.sleep(1)

# 打印子进程的返回码
print('Return code:', p.poll())

在这个示例中,我们启动了一个会运行5秒的子进程。然后,我们每秒调用一次`poll()`方法来检查子进程是否还在运行。当子进程结束时,我们打印出子进程的返回码。

注意,当子进程结束后,你可以通过`Popen.returncode`属性获取子进程的返回码,这和调用`poll()`方法的效果是一样的。

(2)Popen.communicate(input=Nonetimeout=None)

`Popen.communicate()`方法用于和子进程进行交互。具体来说,它会发送输入,读取输出/错误,并等待子进程结束。

以下是参数的详细解释:

- `input`:要发送给子进程的数据。它应该是一个字节串,或者如果`Popen`对象的`text`或`universal_newlines`参数被设为`True`,也可以是一个字符串。如果你不需要发送数据,就不要使用这个参数。

- `timeout`:等待子进程结束的最大时间(以秒为单位)。如果在这个时间内子进程没有结束,就会引发`TimeoutExpired`异常。如果你不希望有时间限制,就不要使用这个参数。

`Popen.communicate()`方法会返回一个元组,包含子进程的标准输出和标准错误。

以下是一些使用示例:

- 读取子进程的输出:

import subprocess

# 创建Popen对象
proc = subprocess.Popen(['ls', '-l'], stdout=subprocess.PIPE)

# 获取命令的输出
out, err = proc.communicate()

# 打印命令的输出
print(out.decode('utf-8'))

- 向子进程发送数据:

import subprocess

# 创建Popen对象
proc = subprocess.Popen(['cat'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)

# 向命令发送数据,并获取命令的输出
out, err = proc.communicate(input=b'hello, world!')

# 打印命令的输出
print(out.decode('utf-8'))

- 使用超时:

import subprocess

# 创建Popen对象
proc = subprocess.Popen(['sleep', '10'], stdout=subprocess.PIPE)

try:
    # 如果子进程在5秒内没有结束,就会引发TimeoutExpired异常
    out, err = proc.communicate(timeout=5)
except subprocess.TimeoutExpired:
    proc.kill()
    out, err = proc.communicate()

(3)Popen.send_signal(signal)

`Popen.send_signal(signal)`方法用于发送信号给子进程。可以使用该方法来控制或者中断子进程。

`signal`参数应该是一个信号常量,如`signal.SIGTERM`或`signal.SIGKILL`。

以下是一个简单的使用示例:

import subprocess
import signal
import time

# 启动一个子进程
p = subprocess.Popen(['sleep', '10'])

# 等待一段时间
time.sleep(5)

# 发送SIGTERM信号给子进程
p.send_signal(signal.SIGTERM)

在这个示例中,我们首先启动了一个`sleep 10`子进程,然后等待5秒,接着发送了一个SIGTERM信号给子进程,告诉它正常退出。如果子进程在收到SIGTERM信号后没有立即退出,我们可以发送SIGKILL信号强制它退出。

注意:在Windows平台上,只有`signal.CTRL_C_EVENT`和`signal.CTRL_BREAK_EVENT`可以被`Popen.send_signal()`发送,但是Python可以接收所有信号。

(4)Popen.terminate()

`Popen.terminate()`方法用于发送一个终止信号给子进程。这个方法并不会立即停止子进程,而是给子进程一些时间来清理并正常退出。

这个方法在Unix和Windows上的行为略有不同:
- 在Unix上,`terminate()`方法会发送一个`SIGTERM`信号给子进程。
- 在Windows上,`terminate()`方法会调用`TerminateProcess()`函数来结束子进程。

这是一个使用示例:

import subprocess
import time

# 启动一个子进程
p = subprocess.Popen(['sleep', '10'])

# 等待一段时间
time.sleep(5)

# 终止子进程
p.terminate()

在这个示例中,我们首先启动了一个`sleep 10`子进程,然后等待5秒,接着调用`terminate()`方法来终止子进程。

注意:虽然`terminate()`方法可以终止子进程,但并不总是能确保子进程被清理。如果需要确保子进程被清理,你应该使用`Popen.communicate()`方法等待子进程结束。

(5)Popen.kill()

`Popen.kill()`方法用于强制结束子进程。与`Popen.terminate()`方法不同,`kill()`方法发送的信号会立即结束子进程,不给子进程清理自身的机会。

这个方法在Unix和Windows上的行为略有不同:
- 在Unix上,`kill()`方法会发送一个`SIGKILL`信号给子进程。
- 在Windows上,`kill()`方法会调用`TerminateProcess()`函数来结束子进程。

这是一个使用示例:

import subprocess
import time

# 启动一个子进程
p = subprocess.Popen(['sleep', '10'])

# 等待一段时间
time.sleep(5)

# 强制结束子进程
p.kill()

在这个示例中,我们首先启动了一个`sleep 10`子进程,然后等待5秒,接着调用`kill()`方法来强制结束子进程。

注意:虽然`kill()`方法可以立即结束子进程,但并不总是能确保子进程被清理。如果需要确保子进程被清理,你应该使用`Popen.communicate()`方法等待子进程结束。

(6)Popen.stdout

`Popen.stdout`是`subprocess.Popen`对象的一个属性,它代表子进程的标准输出流。

当你创建`Popen`对象时,如果你设置了`stdout=subprocess.PIPE`,那么`Popen.stdout`将是一个可读的流对象(实际上是一个`io.BufferedReader`对象),你可以从中读取子进程的输出。

以下是一个使用示例:

import subprocess

# 创建一个子进程,并打开其标准输出流
proc = subprocess.Popen(['ls', '-l'], stdout=subprocess.PIPE)

# 从标准输出流中读取数据
for line in proc.stdout:
    print('Got line:', line.decode('utf-8').strip())

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

在这个示例中,我们创建了一个`ls -l`子进程,并打开了其标准输出流。然后,我们从标准输出流中读取了每一行数据,并打印出来。

注意,`Popen.stdout`只能被读取一次。如果你需要多次读取子进程的输出,你应该将输出保存到一个变量中。同时,如果你打开了子进程的标准输出流,你应该确保读取完所有的输出,否则子进程可能会被阻塞。如果你不需要子进程的输出,你可以设置`stdout=subprocess.DEVNULL`来忽略输出。

(7)Popen.stderr

`Popen.stderr` 是 `subprocess.Popen` 对象的一个属性,它代表子进程的标准错误流。当你创建一个 `Popen` 对象时,如果设置了 `stderr=subprocess.PIPE`,那么 `Popen.stderr` 将是一个可读的流对象(实际上是一个 `io.BufferedReader` 对象),你可以从中读取子进程的错误输出。

以下是一个使用示例:

import subprocess

# 创建一个子进程,并打开其标准错误流
proc = subprocess.Popen(['ls', '/nonexistent'], stderr=subprocess.PIPE)

# 从标准错误流中读取数据
for line in proc.stderr:
    print('Error line:', line.decode('utf-8').strip())

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

在这个示例中,我们创建了一个 `ls /nonexistent` 子进程(这个命令会因为找不到目录而产生错误输出),并打开了其标准错误流。然后,我们从标准错误流中读取了每一行数据,并打印出来。

注意,`Popen.stderr` 只能被读取一次。如果你需要多次读取子进程的错误输出,你应该将输出保存到一个变量中。同时,如果你打开了子进程的标准错误流,你应该确保读取完所有的输出,否则子进程可能会被阻塞。如果你不需要子进程的错误输出,你可以设置 `stderr=subprocess.DEVNULL` 来忽略输出。

(8)Popen.pid

`Popen.pid`是`subprocess.Popen`对象的一个属性,它表示子进程的进程ID(PID)。

当你创建一个`Popen`对象(也就是启动一个子进程)后,你可以通过`Popen.pid`获取子进程的PID。

以下是一个使用示例:

import subprocess

# 创建一个子进程
proc = subprocess.Popen(['sleep', '10'])

# 打印子进程的PID
print('PID:', proc.pid)

在这个示例中,我们启动了一个`sleep 10`子进程,然后打印出了它的PID。

注意,PID是操作系统分配给每个进程的唯一标识符。你可以使用PID来监视或控制进程,比如查看进程的状态、发送信号给进程等。但是,PID是由操作系统管理的,你不应该试图修改PID。

(9)Popen.returncode

`Popen.returncode`是`subprocess.Popen`对象的一个属性,它表示子进程的退出状态码。

当子进程正常结束时,`Popen.returncode`将是子进程的退出状态码,通常情况下,0表示成功,非0表示出错。如果子进程被信号终止,那么返回的将是负的信号值。

以下是一个使用示例:

import subprocess

# 创建一个子进程
proc = subprocess.Popen(['ls', '-l'])

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

# 打印子进程的退出状态码
print('Return code:', proc.returncode)

在这个示例中,我们启动了一个`ls -l`子进程,然后等待它结束,最后打印出了它的退出状态码。

注意,在子进程还没有结束时,`Popen.returncode`的值是`None`。你可以使用`Popen.poll()`方法来检查子进程是否已经结束,如果子进程已经结束,`poll()`方法将返回退出状态码,否则返回`None`。

4、subprocess.check_call

`subprocess.check_call()`函数用于运行一个命令,如果命令成功执行(即返回码为0),则返回0,否则抛出`CalledProcessError`异常。

(1)函数参数列表

subprocess.check_call(args*stdin=Nonestdout=Nonestderr=Noneshell=Falsecwd=Nonetimeout=None**other_popen_kwargs)

(2)参数详解

1、`args`:要运行的命令和参数,可以是一个列表(如`["ls", "-l"]`),也可以是一个字符串(如`"ls -l"`)。如果`args`是一个字符串,那么需要设置`shell=True`。

2、`stdin`, `stdout`, `stderr`:分别表示子进程的标准输入、输出和错误流。默认情况下,这三个流都会被继承自父进程。如果你想重定向这些流,可以设置为`subprocess.PIPE`或一个文件对象。

3、 `shell`:如果设置为`True`,那么命令将在一个shell中运行,这意味着你可以使用shell特性,如通配符、管道等。默认情况下,`shell`是`False`。

-4、`cwd`:设置子进程的当前工作目录。

5、`timeout`:设置子进程的超时时间(单位是秒)。如果在这个时间内子进程没有结束,就会引发`TimeoutExpired`异常。

6、 `other_popen_kwargs`:其他传递给`Popen`构造函数的关键字参数。

(3)使用示例

1、 简单的命令:

import subprocess

# 运行ls -l命令
subprocess.check_call(["ls", "-l"])

2、 使用shell特性:

import subprocess

# 运行ls -l | grep .py命令
subprocess.check_call("ls -l | grep .py", shell=True)

3、 捕获输出:

import subprocess

# 运行ls -l命令,并捕获输出
with open('output.txt', 'w') as f:
    subprocess.check_call(["ls", "-l"], stdout=f)

请注意,`check_call()`函数会等待命令结束。如果你需要异步执行命令,你应该使用`Popen`类。

5、subprocess.check_output

subprocess.check_output()函数用于运行一个命令,并返回命令的输出。如果命令成功执行(即返回码为0),则返回命令的输出,否则抛出CalledProcessError异常。

(1)函数参数列表

subprocess.check_output(args*stdin=Nonestderr=Noneshell=Falsecwd=Noneencoding=Noneerrors=Noneuniversal_newlines=Nonetimeout=Nonetext=None**other_popen_kwargs)

(2)参数详解

1、 `args`:要运行的命令和参数,可以是一个列表(如`["ls", "-l"]`),也可以是一个字符串(如`"ls -l"`)。如果`args`是一个字符串,那么需要设置`shell=True`。

2、`stdin`, `stderr`:分别表示子进程的标准输入和错误流。默认情况下,这两个流都会被继承自父进程。如果你想重定向这些流,可以设置为`subprocess.PIPE`或一个文件对象。

3、`shell`:如果设置为`True`,那么命令将在一个shell中运行,这意味着你可以使用shell特性,如通配符、管道等。默认情况下,`shell`是`False`。

4、`cwd`:设置子进程的当前工作目录。

5、`encoding`, `errors`:这两个参数用于控制如何解码命令的输出。如果设置了这两个参数,那么命令的输出将被解码为字符串,否则输出为字节串。

6、`universal_newlines`:这个参数已经被`text`参数取代,不建议使用。

7、`timeout`:设置子进程的超时时间(单位是秒)。如果在这个时间内子进程没有结束,就会引发`TimeoutExpired`异常。

8、`text`:如果设置为`True`,那么命令的输出将被解码为字符串,否则输出为字节串。

9、 `other_popen_kwargs`:其他传递给`Popen`构造函数的关键字参数。

(3)使用示例

1、简单的命令:

import subprocess

# 运行ls -l命令,并获取输出
output = subprocess.check_output(["ls", "-l"], text=True)
print(output)

2、使用shell特性:

import subprocess

# 运行ls -l | grep .py命令,并获取输出
output = subprocess.check_output("ls -l | grep .py", shell=True, text=True)
print(output)

请注意,`check_output()`函数会等待命令结束。如果你需要异步执行命令,你应该使用`Popen`类。

你可能感兴趣的:(python库学习,python)