深入理解Python sys.stdin
sys.stdout
玩转命令行
引言
在我们玩命令行时,我们总会使用到从窗口获取字符,或者从窗口输出字符,其实这些都能被我们的python程序截取到,对其进行一定操作转换后又输出到控制台,这节我们就深度理解一下sys.stdin
sys.stdout
玩转命令行
简单例子引入
新建一个 teststreams.py
和 input.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数据,因此必须解码才可以
更深入的了解
- 使用
print
时sys.stdout
会自动调用write
- 使用
input
时sys.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
的味道,本节有一定难度,希望读者可以自行运行代码深入理解一下,读者更需要对命令行一些命令有一定的了解