得之我幸 失之我命

when someone abandons you,it is him that gets loss because he lost someone who truly loves him but you just lost one who doesn’t love you.

subprocess 的 Popen 的使用

记录下 python 中 subprocess.Popen 的一些常用参数和函数

常用参数详解

args

可以是一个字符串,可以是一个包含程序参数的列表。要执行的程序一般就是这个列表的第一项,或者是字符串本身

1
2
subprocess.Popen(["cat", "test.txt"])
subprocess.Popen("cat test.txt")

这两个之中,后者将不会工作。因为如果是一个字符串的话,必须是程序的路径才可以。(考虑 unix 的 api 函数 exec,接受的是字符串列表)

但是下面的可以工作

1
subprocess.Popen("cat test.txt", shell=True)

这是因为它相当于

1
subprocess.Popen(["/bin/sh", "-c", "cat test.txt"])

在 *nix 下,当 shell=False(默认)时,Popen 使用 os.execvp() 来执行子程序。args 一般要是一个列表。如果 args 是个字符串的
话,会被当做是可执行文件的路径,这样就不能传入任何参数了;当 shell=True 时,如果 arg 是个字符串,就使用 shell 来解释执行这个字符串。如果 args 是个列表,则第一项被视为命令,其余的都视为是给 shell 本身的参数

在Windows下,下面的却又是可以工作的

1
2
subprocess.Popen(["notepad.exe", "test.txt"])
subprocess.Popen("notepad.exe test.txt")

这是由于 windows 下的 api 函数 CreateProcess 接受的是一个字符串。即使是列表形式的参数,也需要先合并成字符串再传递给 api 函数

1
subprocess.Popen("notepad.exe test.txt", shell=True)

等同于

1
subprocess.Popen("cmd.exe /C " + "notepad.exe test.txt", shell=True)

bufsize

如果指定了 bufsize 参数作用就和内建函数 open() 一样:0 表示不缓冲,1 表示行缓冲,其他正数表示近似的缓冲区字节数,负数表示使用系统默认值

executable

指定要执行的程序。它很少会被用到:一般程序可以由 args 参数指定。如果 shell=True,executable 可以用于指定用哪个 shell 来执行(比如 bash、csh、zsh 等)。*nix下,默认是 /bin/sh,windows 下,就是环境变量 COMSPEC 的值。windows 下,只有要执行的命令确实是 shell 内建命令(比如dir,copy 等)时,才需要指定 shell=True,而要执行一个基于命令行的批处理脚本的时候,不需要指定此项

stdin, stdout, stderr

分别表示子程序的标准输入、标准输出和标准错误。可选的值有 PIPE 或者一个有效的文件描述符(其实是个正整数)或者一个文件对象,还有 None。如果是 PIPE,则表示需要创建一个新的管道,如果是 None,不会做任何重定向工作,子进程的文件描述符会继承父进程的。另外,stderr 的值还可以是 STDOUT,表示子进程的标准错误也输出到标准输出

preexec_fn

如果把 preexec_fn 设置为一个可调用的对象(比如函数),就会在子进程被执行前被调用(仅限*nix)

close_fds

如果把 close_fds 设置成 True,*nix 下会在开子进程前把除了 0、1、2 以外的文件描述符都先关闭。在 Windows 下也不会继承其他文件描述符

shell

如果把 shell 设置成 True,指定的命令会在 shell 里解释执行

cwd

如果 cwd 不是 None,则会把 cwd 做为子程序的当前目录。注意,并不会把该目录做为可执行文件的搜索目录,所以不要把程序文件所在目录设置为 cwd

env

如果 env 不是 None,则子程序的环境变量由 env 的值来设置,而不是默认那样继承父进程的环境变量。注意,即使你只在 env 里定义了某一个环境变量的值,也会阻止子程序得到其他的父进程的环境变量(也就是说,如果 env 里只有 1 项,那么子进程的环境变量就只有 1 个了)

universal_newlines

如果把 universal_newlines 设置成 True,则子进程的 stdout 和 stderr 被视为文本对象,并且不管是 *nix 的行结束符 /n,还是老 mac 格式的行结束符 /r,还是 windows 格式的行结束符 /r/n 都将被视为 /n

startupinfo, creationflags

如果指定了 startupinfo 和 creationflags,将会被传递给后面的 CreateProcess() 函数,用于指定子程序的各种其他属性,比如主窗口样式或者是子进程的优先级等(仅限Windows)

subprocess.PIPE

subprocess.PIPE

一个可以被用于 Popen 的 stdin 、stdout 和 stderr 3 个参数的特殊值,表示需要创建一个新的管道

subprocess.STDOUT

一个可以被用于 Popen 的 stderr 参数的输出值,表示子程序的标准错误汇合到标准输出

方便的函数

subprocess.call

执行命令,并等待命令结束,再返回子进程的返回值

subprocess.check_call

执行上面的 call 命令,并检查返回值,如果子进程返回非 0,则会抛出 CalledProcessError 异常,这个异常会有个 returncode 属性,记录子进程的返回值

check_output

执行程序,并返回其标准输出

Popen 对象

方法

  1. Popen.poll()

    检查子进程是否已结束,设置并返回 returncode 属性

  2. Popen.wait()

    等待子进程结束,设置并返回 returncode 属性

    注意: 如果子进程输出了大量数据到 stdout 或者 stderr 的管道,并达到了系统 pipe 的缓存大小的话,子进程会等待父进程读取管道,而父进程此时正 wait 着的话,将会产生传说中的死锁,后果是非常严重滴。建议使用 communicate() 来避免这种情况的发生

  3. Popen.communicate()

    和子进程交互:发送数据到 stdin,并从 stdout 和 stderr 读数据,直到收到 EOF。等待子进程结束。可选的 input 如有的话,要为字符串类型
    此函数返回一个元组:(stdoutdata, stderrdata)
    注意: 要给子进程的 stdin 发送数据,则 Popen 的时候,stdin 要为 PIPE;同理,要可以接收数据的话,stdout 或者 stderr 也要为 PIPE

  4. Popen.send_signal(signal)

    给子进程发送 signal 信号
    注意: windows 下目前只支持发送 SIGTERM,等效于下面的 terminate()

  5. Popen.terminate()

    停止子进程。Posix 下是发送 SIGTERM 信号。windows 下是调用 TerminateProcess() 这个API

  6. Popen.kill()

    杀死子进程。Posix 下是发送 SIGKILL 信号。windows 下和 terminate() 无异

  7. Popen.stdin

    如果 stdin 参数是 PIPE,此属性就是一个文件对象,否则为 None

  8. Popen.stdout

    如果stdout参数是PIPE,此属性就是一个文件对象,否则为None

  9. Popen.stderr

    如果stderr 参数是PIPE,此属性就是一个文件对象,否则为None

  10. Popen.pid

    子进程的进程号。注意,如果shell 参数为True,这属性指的是子shell的进程号

  11. Popen.returncode

    子程序的返回值,由poll()或者wait()设置,间接地也由communicate()设置。

    如果为None,表示子进程还没终止。

    如果为负数-N的话,表示子进程被N号信号终止。(仅限*nux)

用 subprocess 来代替其他函数

代替 shell 命令

1
p = `ls -l`

等效于

1
p = Popen(['ls','-l'], stdout=PIPE).communicate()[0]

代替 shell 管道

1
p = `dmesg | grep cpu`

等效于

1
2
3
p1 = Popen(['dmesg'], stdout=PIPE)
p2 = Popen(['grep', 'cpu'], stdin=p1.stdout, stdout=PIPE)
output = p2.communicate()[0]

代替 os.system()

1
2
3
4
lsl = os.system('ls '+'-l')  # 这个是一个返回状态
# 等效于
p = Popen('ls -l', shell=True)
lsl = os.waitpid(p.pid,0)[1]

代替 os.spawn 系列

  1. P_NOWAIT 的例子
    1
    2
    3
    pid = os.spawnlp(os.P_NOWAIT, “/bin/mycmd”, “mycmd”, “myarg”)
    # 等效于
    pid = Popen(["/bin/mycmd", "myarg"]).pid
  2. P_WAIT 的例子
    1
    2
    3
    retcode = os.spawnlp(os.P_WAIT, “/bin/mycmd”, “mycmd”, “myarg”)
    # 等效于
    retcode = call(["/bin/mycmd", "myarg"])
  3. 返回值处理
    1
    2
    3
    4
    5
    6
    7
    8
    9
    pipe = os.popen('cmd', 'w')
    rc = pipe.close()
    if rc != None and rc % 256:
    print “There were some errors”
    # 等效于
    process = Popen('cmd', 'w', shell=True, stdin=PIPE)
    process.stdin.close()
    if process.wait() != 0:
    print “There were some errors”

be slow to promise and quick to perform.