注: 本文中代码均在shell中执行.
os.system 输出命令结果到屏幕, 返回命令执行状态.
若想将os.system的命令结果保存为一个变量, 要用到popen.
os.popen("dir").read() # 会保存命令的执行结果输出
要同时得到命令结果和命令执行状态, 需要用到subprocess.
subprocess
- 目的: 代替一些老的模块, 如os.system, os.spawn*.
- python 3.5之后才出现subprocess
- 推荐的调用subprocess模块方法: 通过调用 run()方法来实现
subprocess常用方法
1. subprocess.Popen
- 接收字符串格式命令, 返回元组形式, 第一个元素是执行状态, 第二个是命令结果.
- subprocess.getstatusoutput('ls/bin/ls'), 返回 (0, '/bin/ls)
- 该方法底层封装的是subprocess.Popen方法
- 若要输出命令结果在屏幕上, 需要这样写 (以下是在shell里执行的脚本)
- res = subprocess.Popen("ifconfig|grep 192", shell=True,stdout=subprocess.PIPE)
- res.stdout.read() 注: 若res里没有加stdout=subprocess.PIPE, 直接res.stdout是读不出来结果的(stdout代表标准输出). 相当于把命令结果存储在PIPE里(在内存里以管道的形式存储), 这样读取的时候就可以读出来. 每执行一个命令都会开辟一个新的管道, 所以不会堵.
- 如果执行错误, 想要抓到错误信息, 需要再添加stderr (以下是在shell里执行的脚本):
-
res = subprocess.Popen("ifconfig|grep 192", shell=True,stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-
res.stderr.read()
-
2. poll()
- check if child process has terminated. returns returncode.
- 每次调subprocess命令执行shell命令, 都相当于启了一个新的shell环境(相当于一个新的应用程序/一个新的进程)
- 若一个进程太长, 不知道什么时候会执行完毕, 就需要定时去下, 就用到pulll.
- res = subprocess.Popen("sleep 10; echo 'hello'", shell=True,stdout=subprocess.PIPE)
- res.stdout.read() 需要等到10秒之后才会返回结果
- 因为不知道res什么时候执行完, 就定时去检查一下, 一旦执行完, 就去调结果:
- res.poll() 返回NONE, 就是还没执行完; 返回0, 就是已经执行完了, 可以读结果了.
3. wait()
- wait for child process to terminate. returns returncode attribute.
- 和pull()有点像, 也是检查是否执行完毕, 但是wait()会一直等, 直到执行完毕之后才会返回0代表已经出结果了; 不会像pull()一样, 若没执行完也会返回一个状态.
4. terminate()
- 杀掉所有启动进程
- 下面这段代码什么也读不出来, 因为进程中间被杀掉了:
- res = subprocess.Popen("sleep 10; echo 'hello'", shell=True,stdout=subprocess.PIPE)
- res.terminate()
- res.stdout.read()
5. stdin()
- 标准输入
import subprocessobj = subprocess.Popen(["python"], stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE)obj.stdin.write('print 1\n') obj.stdin.write('print 2\n') obj.stdin.write('print 3\n') obj.stdin.write('print 4\n') out_error_list = obj.communicate(timeout=10)print out_error_list
- 例:
- res = subprocess.Popen(['python3], shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,stdin=subprocess.PIPE)
- res.stdin.write(b"print(1)") 必须在打印的内容前面加b, 代表byte类型, 否则会出错.
- res.stdin.write(b"print(2)")
- res.stdin.write(b"print(3)")
- res.stdin.write(b"print(4)")
- res.communicate() 把前面写的内容一次性读取出来
subprocess常用参数
1. args
- shell 命令, 可以是字符串或者序列类型(如: list, 元组)
2. bufsize
- 指定缓冲. 0 -> 无缓冲, 1 -> 行缓冲, 其他 -> 缓冲区大小, 负值 -> 系统缓冲.
3. preexec_fn
- 只在Unix平台下有效, 用于指定一个可执行对象(callable object)
4. close_fds
- windows平台下, 如果close_fds被设置为True, 则新创建的子进程将不会继承父进程的输入,输出, 错误管道.
5. shell
- if shell is True, the specified command will be excuted through the shell.
6. pwd
- 用于设置子进程的当前目录
- 例:
- res = subprocess.Popen(['pwd'], shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE, pwd="/tmp")
- res.stdout.read() 返回b'/tmp\n'
7. env
- 用于指定子进程的环境变量. 如果env=NONE,子进程的环境变量将从父进程中继承.
8. universal_newlines
- 不同系统的换行符不同,True -> 同意使用\n.
9. startupinfo与createionflags
- 只在windows下有效
- 将被传递给底层的CreateProcess()函数, 用于设置子进程的一些属性, 如: 主窗口的外观,进程的优先级等.
终端输入的命令分为两种:
- 输入即可得到输出, 如: ifconfig
- 输入进行某环境, 依赖再输入, 如: python
sudo自动输密码
import subprocessdef mypass(): mypass = '123' # get the password from anywhere return mypassecho = subprocess.Popen(['echo', mypass()], stdout=subprocess.PIPE, )sudo = subprocess.Popen(['sudo', '-S', 'iptables', '-L'], stdin=echo.stdout, studout=subprocess.PIPE, )end_of_pipe = sudo.stdoutprint "Passowrd ok \n Iptables Chains %s" % end_of_pipe.read()
以上是在linux里执行, 还可以通过python执行
注: 上图通过python执行时, '123'要用单引号, 不能用双引号.