1.运行外部命令
1.要运行一个外部命令,可以采用os.system() 交互,可以使用run() 函数
import subprocess
completed = subprocess.run(['ls','-l'])
print('returncode: ',completed.returncode)
'''
命令行参数作为字符串列表传入,run() 返回一个CompletedProcess实例,它包含进程的有关信息,退出码和输出
'''
2.将shell 参数设置为true值会使得 subprocess 创建一个中间 shell 进程,由这个进程运行命令,默认情况下会直接运行命令
import subprocess
completed = subprocess.run('echo $HOME',shell=True)
print('returncode:',completed.returncode)
# 使用一个中间shell意味着运行命令之前会先处理命令行中的变量,glob模式以及其他特殊shell特性
# 说明:如果使用 run() 而没有传入check=True,这等价于使用call(),只返回进程的退出码
3.错误处理:CompletedProcess的returncode 属性是程序的退出码.调用者负责解释这个返回值.如果run()的check 参数为True 则会检测退出码. 若发生一个错误,则会产生一个CalledProcessError异常.
import subprocess
try:
	subprocess.run(['false'],check=True)
except subprocess.CalledProcessError as err:
	print('Error:',err)
# false 命令退出时总有一个非0的状态码,run() 会把它解释为一个错误.
4.捕获输出:
	对于run() 启动的进程,它的标准输入和标准输出通道会被绑定到父进程的输入和输出中,这说明调用程序无法捕获命令的输出。可以通过stdout 和stderr 参数传入 PIPE 来捕获输出,以备以后处理
import subprocess
completed = subprocess.run(['ls','-l'],stdout=subprocess.PIPE)
print('returncode:',completed.returncode)
print("Have {} bytes in stdout:\n{}".format(len(completed.stdout),completed.stdout.decode('utf-8')))
# 说明: 传入check=True 并设置 stdout 为PIPE 等价于使用 check_output()
import subprocess
try:
	completed = subprocess.run(
	'echo to stdout;echo to stderr 1>&2;exit 1',
	check = True,
	shell = True,
	stdout = subprocess.PIPE
	)
except subprocess.CalledProcessError as err:
	print('ERROR:',err)
else:
	print('reurncode:',completed.returncode)
	print('Have {} bytes in stdout:{!r}'.format(
		len(completed.stdout),
		completed.stdout.decode('utf-8')
		))
# 发送到标准错误输出的消息 会被打印到控制台,而发送到标准输出的消息会被隐藏.
# 为了避免通过run() 运行的命令将错误消息写至 控制台,可以设置 stderr 参数为PIPE
import subprocess
try:
	completed = subprocess.run(
		'echo to stdout;echo to stderr 1>&2;exit 1',
		shell = True,
		stdout = subprocess.PIPE,
		stderr = subprocess.PIPE,
	)
except subprocess.CalledProcessError as err:
	print('Error:',err)
else:
	print("returncode:",completed.returncode)
	print("Have {} bytes in stdout:{!r}".format(
			len(completed.stdout),
			completed.stdout.decode('utf-8')
		))
	print('Have {} bytes in stderr:{!r}'.format(
			len(completed.stderr),
			completed.stderr.decode('utf-8')
		))
# 本例子中没有设置 check = True 所以不会捕获异常
# 使用 check_output() 时如果要捕获错误消息,则要把stderr 设置为STDOUT 错误消息将与命令的其他输出合并在一起
import subprocess
try:
	output = subprocess.check_output(
		'echo to stdout;echo to stderr 1>&2',
		shell = True,
		stderr = subprocess.STDOUT,
	)
except subprocess.CalledProcessError as err:
	print('Error:',err)
else:
	print("Have {} bytes in output: {!r}".format(len(output),output.decode('utf-8')))
5.抑制输出:
	在不能显示或捕获输出的情况下,可以使用DEVNULL 抑制输出
import subprocess
try:
	completed = subprocess.run(
		'echo to stdout;echo to stderr 1>&2;exit 1',
		shell = True,
		stdout = subprocess.DEVNULL,
		stderr = subprocess.DEVNULL
	)
except subprocess.CalledProcessError as err:
	print('Error:',err)
else:
	print("returncode:",completed.returned)
	print("stdout is {!r}".format(completed.stdout))
	print("stderr is {!r}".format(completed.stderr))
# DEVNULL 的名字来自于UNIX 特殊设备文件 /deb/null,打开文件读取时,DEVNULL 对应文件末尾,写文件时会接收并忽略所有输入。
6.直接处理管道
# 与进程的单向通信
import subprocess
print('read:')
proc = subprocess.Popen(
	['echo',"to stdout"],
	stdout = subprocess.PIPE,
)
stdout_value = proc.communicate()[0].decode('utf-8')
print("stdout:",repr(stdout_value))
# 要运行一个进程并读取它的所有输出,可以设置stdout值PIPE并调用communicate()

# 要建立一个管道,以便调用程序写数据,可以设置 stdin 为PIPE
import subprocess
print("write:")
proc = subprocess.Popen(
	['cat','-'],
	stdin = subprocess.PIPE,
	)
proc.communicate('stdin: to stdin\n'.encode('utf-8'))
# 要将数据一次性发送到进程的标准输入通道,可以把数据传递到 communicate() 这类似于使用 popen() 并指定 'w' 模式

# 进程双向通信
import subprocess
proc = subprocess.Popen(
	['cat','-'],
	stdin = subprocess.PIPE,
	stdout = subprocess.PIPE
)
msg = 'through stdin to stdout'.encode('utf-8')
stdout_value = proc.communicate(msg)[0].decode('utf-8')
print("pass through:",repr(stdout_value))

# 捕获错误和输出
# 监视 stdout stderr 数据流
import subprocess
proc = subprocess.Popen(
	'cat -;echo "to stderr" 1>&2',
	shell = True,
	stdin = subprocess.PIPE,
	stdout = subprocess.PIPE,
	stderr = subprocess.PIPE,
) 
msg = 'through stdin to stdout'.encode('utf-8')
stdout_value,stderr_value = proc.communicate(msg)
print('pass through:',repr(stdout_value.decode('utf-8')))
print('stderr	:',repr(stderr_value.decode('utf-8')))
# 从 stderr 读取数据与从 stdout 读取时一样的传入 PIPE 会告诉Popen关联到通道, communicate() 在返回之前从这个通道读取所有数据

# 错误输出从定向到标准输出通道, stderr 要使用STDOUT 而不是PIPE
import subprocess
proc = subprocess.Popen(
		'cat -;echo "to stderr" 1>&2',
		shell = True,
		stdin = subprocess.PIPE,
		stdout = subprocess.PIPE,
		stderr = subprocess.STDOUT,
	)
msg = 'through stdin to stdout\n'.encode('utf-8')
stdout_value,stderr_value = proc.communicate(msg)
print('combined output:',repr(stdout_value.decode('utf-8')))
print('stderr value	:',repr(stderr_value))