Python,Shell 和 三个标准文件

场景

使用 Python 执行 Shell 命令(或者脚本),有两种执行场景:

  1. 等待,直到命令执行完毕,一次性获取返回结果,做一些你想做的事情;
  2. 命令执行的同时,实时获取命令的持续输出,做一些你想做的事情。

例子

第一种场景:ls -a (list segment,Unix系统中使用非常频繁的命令)用于列出所有文件,文件列出之后就自动退出了。

第二种场景:ping zhihu.com 它会持续的输出结果,并不会退出(当然可以加 -t n 来指定 n 次之后结束退出,这样就属于场景一了,本文我们不指定 n)

相信这两种情况已满足了读者 90% 呃不 100% 需求了,如果没有请留言区留言


预备

执行一个 Shell 命令行时通常会自动打开三个标准文件,即:

  • 标准输入文件(stdin),通常对应终端的键盘
  • 标准输出文件(stdout)
  • 标准错误输出文件(stderr)

后两个文件对应终端的屏幕,进程从标准输入文件中得到输入数据,将正常输出数据输出到标准输出文件,而将错误信息送到标准错误文件中。

实战

比较一下比较好记忆:

ls -a (场景一)

import subprocess

def run_cmd(cmd):
    return subprocess.Popen(
        cmd,
        shell=False,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE).communicate()

std_out_and_err = run_cmd(['ls', '-a'])
print(std_out_and_err)
# ('.\n..\ncontinuegetstdout.py\n', '')

两个点比较有价值:

  1. shell 这个参数很多人不理解,其实就是 False 的时候 Python 帮你执行命令, True 的时候相当于直接在终端执行命令。False 的时候,我们需要把命令按空格使用逗号分隔开来(即 list 数据结构)传给 cmd 参数(目的是让 Python 清楚这条命令的所有细节),代码中的例子就是使用这种;而 True 的时候只需要把命令一股脑(string 数据结构)的传给 cmd 参数,总结即 False:cmd=['ls', '-a'],True:cmd='ls -a' 纠结吗?推荐第一种
  2. communicate() 这个方法到底是干嘛的?官方文档如下:

Interact with process: Send data to stdin. Read data from stdout and stderr, until end-of-file is reached. Wait for process to terminate. The optional input argument should be a string to be sent to the child process, or None, if no data should be sent to the child. communicate() returns a tuple (stdout, stderr).

翻译一下:

与进程进行交互:将数据发送到 stdin。从 stdout 和 stderr 文件中读取数据,直到达到文件结尾。等待进程终止。可选的 input 参数应该是要发送到子进程的字符串,如果没有数据应该发送给子进程,则为 None。 communicate() 返回一个元组 (stdout, stderr)。

例子中我们并没有显式的指定 input 参数,默认为 None,我们只从 stdin 和 stderr 文件中读取数据。

这里其实是一个同步的过程,进程终止后才会返回所读取到的数据(进程终止,文件自然也结尾了),也就是 communicate() 方法直接使命令执行变为了同步,不执行完成就一直阻塞。如果我们执行 ping zhihu.com 等不会自己终止的命令,这种方式会一直卡死,因为进程一直没有结束,文件也一直不会结尾。

那么类似这种持续输出结果的命令如何执行呢?我们接着向下看

ping zhihu.com (场景二)

import subprocess

def run_cmd(cmd):
    return subprocess.Popen(
        cmd,
        shell=False,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE)

p = run_cmd(['ping', 'zhihu.com'])
for i in iter(p.stdout.readline, ''):
    print(i.strip())

如代码所示,少了 communicate(),于是 p 是 subprocess.Popen 类的实例

查看 Popen 类源码 738 行:

self.stdout = os.fdopen(c2pread, 'rU', bufsize)

os.fdopen 的文档:

Return an open file object connected to a file descriptor.

得知 p.stdout 是一个:打开的文件对象,那么用 readline 就合理了。

注意这里使用了 iter() 内置函数,将 p.stdout 转换为一个迭代器,并使用 p.stdout.readline 替换迭代器的 next 方法,后面 '' 的意思就是当 p.stdout.readline 返回的值是 '' 的时候,迭代器终止,本篇不详细讲,有兴趣可以留言提问哦

原文发布于微信公众号 - 随心DevOps(heart-devops)

原文发表时间:2018-01-21

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏C/C++基础

Shell特殊字符

shell既是类Unix操作系统的命令解析器,用于解释执行用户输入的一连串命令,它类似于DOS下的command和后来Windows的cmd.exe。同时she...

721
来自专栏各种机器学习基础算法

原 crontab使用

1493
来自专栏张善友的专栏

Remoting: Server encountered an internal error

发生这个错误的原因是Remoting的一个配置项: <customErrors> 元素   指示此应用程序域中的服务器通道是否将经筛选的或完整的异常信息返回给本...

1647
来自专栏大神带我来搬砖

在log4j2中灵活切换输出日志的格式

使用log4j2的过程中,日志是安装固定格式输出的。这个格式是pattern所定义的,例如 %d{dd MMM yyyy HH:mm:ss,SSS}: %m%n...

2846
来自专栏Python小屋

Python标准库shutil中rmtree()使用回调函数

这段代码目的是删除包含只读文件的文件夹,主要演示回调函数的用法。 >>> import os >>> import stat >>> import shutil...

3169
来自专栏北京马哥教育

线上应用故障排查之一:高CPU占用

线上应用故障排查之一:高CPU占用 一个应用占用CPU很高,除了确实是计算密集型应用之外,通常原因都是出现了死循环。 以我们最近出现的一个实际故障为例,介绍怎么...

2766
来自专栏流媒体

C语言文件操作 stat,fseek,copy

作者:简书 链接:http://www.jianshu.com/p/q81RER 來源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注...

622
来自专栏ml

Linux GDB常用命令一栏

Linux GDB 常用命令如下: 1.启动和退出gdb (1)启动:gdb ***:显示一段版权说明; (*** 表示可执行程序名) (2)退出:quit。有...

3424
来自专栏决胜机器学习

《Redis设计与实现》读书笔记(三十六) ——Redis 慢查询日志实现

《Redis设计与实现》读书笔记(三十六) ——Redis 慢查询日志实现 (原创内容,转载请注明来源,谢谢) 一、基本功能 redis的慢查询日志,用于记录执...

3064
来自专栏开发与安全

linux系统编程之进程(二):fork函数相关总结

fork的作用是根据一个现有的进程复制出一个新进程,原来的进程称为父进程(Parent Process),新进程称为子进程(Child Process)。系统中...

2706

扫码关注云+社区