深入理解Python sys.stdin sys.stdout 玩转命令行

深入理解Python sys.stdin sys.stdout 玩转命令行

引言

在我们玩命令行时,我们总会使用到从窗口获取字符,或者从窗口输出字符,其实这些都能被我们的python程序截取到,对其进行一定操作转换后又输出到控制台,这节我们就深度理解一下sys.stdin sys.stdout 玩转命令行

简单例子引入

新建一个 teststreams.pyinput.txt 文件

  • window中使用type查看文件的内容
  • liunx中使用cat
# teststreams.py
def interact():
    print('hello stream world') # sys.stdout
    while True:
        try:
            reply = input('Enter a number') # sys.stdin
        except EOFError:
            break
        else:
            num = int(reply)
            print('%s squared is %d' % (num, num**2))
    print('Bye')
if __name__ == '__main__':
    interact()

在这里插入图片描述
使用shell

std_.py < input.txt
# 或者使用
type input.txt | std_.py

那么input.txt里面的内容就会被sys.stdin截获,input一次就会截获一次,后面讲解截获背后的真相,这里并没有看出sys.stdout有什么作用

观察实验的结果

在这里插入图片描述
实验代码里的input并没有让我们输入数据,而自动截获到了重定向过来的数据,另外数据如果没有了,会发生报错,说明input已经不再受理shell输入的参数了

进一步理解

上一步我们发现input似乎已经失去原有的意义了,对shell已经不再受理了,所以验证我们的猜想是不是正确的
input相当于sys.stdin.readline(),如果想要一次读取所有那么使用sys.stdin.read()

import sys
def getreply():
    if sys.stdin.isatty():
        return input('?')
    else:
        import msvcrt
        msvcrt.putch(b'?')
        key = msvcrt.getch()
        msvcrt.putch(b'\n')
        return key.decode()
def more(text: str, numlines=10):
    lines = text.splitlines()
    while lines:
        chunk = lines[:numlines]
        lines = lines[numlines:]
        for line in chunk: print(line)
        if lines and getreply() not in ['y', 'Y']: break
if __name__ == '__main__':
    if len(sys.argv) == 1:
        more(sys.stdin.read())
    else:
        more(open(sys.argv[1]).read())

通过sys.stdin.read()获取重定向传入的所有数据, 如果传入的是命令行模式sys.stdin.isatty()True,而False就会直接导致input不可获取shell传入的数据
会报如下错误
在这里插入图片描述
如果我们还想获取从shells输入的数据那么就必须用到msvcrt模块,使用putch用于输出显示,传入bytes数据,而想实现原来input数据就必须使用getch,依然得到的是bytes数据,因此必须解码才可以
在这里插入图片描述

更深入的了解

  • 使用printsys.stdout会自动调用write
  • 使用inputsys.stdin会自动调用read
    读者可以自行思考一下这个程序的运行结果
import sys

class Output:
    def __init__(self):
        self.text = ''

    def write(self, line):
        self.text += line

    def writelines(self, lines):
        [self.write(line) for line in lines]

    def flush(self):
        self.text = ''
class Input:
    def __init__(self, input):
        self.text = input

    def read(self):
        return self.text

    def readline(self):
        lines = self.text.find('\n')
        if lines == -1:
            return self.text
        else:
            return self.text[:lines+1]
# 重定向,输入
savesteams = sys.stdin, sys.stdout
sys.stdin = Input('input')
sys.stdout = Output()
text = input('>>')
print(text)
print('output')
out = sys.stdout.text
sys.stdin, sys.stdout = savesteams
print(out)
  • 由于input()默认会调用sys.stdin.read(), 因此input()获取到 input
  • 由于print()默认会调用sys.stdin.write(),由于我们已经替换了原来的write,现在就不会打印任何信息了,而倒数第二步还原,print()又可以正常打印数据了

总结

本节如果理解了那么我们离玩转命令行又进了一步,可以自行控制输入和输出,有点钩子hook的味道,本节有一定难度,希望读者可以自行运行代码深入理解一下,读者更需要对命令行一些命令有一定的了解

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值