⌨️ 第二章 编程语言都有什么不同
📄 一句话概括

每个程序一出生就自带三条数据通道——标准输入(喂给它)、标准输出(说结果)、错误输出(报异常)——把【正常结果】与【错误信息】分两条路走,是命令行世界运转五十年的根基。

📝 本节和前面几节的关系

前面讲的是【语言内部怎么组织】,本节讲【程序与外界的边界】:这是你在命令行(树莓派课第四章的终端机、服务器课的 SSH)里天天见到的重定向与管道的原理层——工具你会用了,本节补【为什么】。

一、三条标准流:程序的三根水管

Unix 的设计者给每个程序默认接好三根管子:

通道 编号 默认去向 装什么
标准输入(stdin) 0 键盘 程序要读的数据
标准输出(stdout) 1 屏幕 正常结果
标准错误(stderr) 2 屏幕 错误信息、日志

关键设计是第 2 号与第 1 号虽然都显示在屏幕上,却是两条独立的管子——正因为分家,【结果】才能被干净地送走而【错误】留在眼前(下文重定向一节看它的威力)。

各语言的对应写法大同小异:

# Python
name = input()                # 读标准输入
print("你好,", name)          # 写标准输出
import sys
sys.stderr.write("出错了!\n")  # 写标准错误
// C++(回收 14 课 2.1.2 iostream)
std::cin >> name;             // stdin
std::cout << "你好" << name;   // stdout
std::cerr << "出错了!";       // stderr(不带缓冲)

二、重定向与管道:把水管接到别处

命令行里的 <、>、| 就是在重新接线:

python demo.py > result.txt      # stdout 改道进文件(屏幕上看不到结果了)
python demo.py 2> error.log      # stderr 改道,屏幕只剩正常输出
python demo.py > all.txt 2>&1    # 两条管子都进同一个文件
python demo.py | sort            # 管道:本程序的 stdout 接到 sort 的 stdin
grep 错误 app.log | wc -l        # 管道可以串联:找错误行,再数有几行

这就是 Unix 哲学的核心:每个程序只做一件事、做好一件事,靠管道互相拼装——grep、sort、wc 各自无名小卒,串起来就是一个报表系统。你在树莓派课第四章用过的 ls | grep、cat x | head,源头都是这套【三管子】设计。

三、为什么要分家:一个真实的坑

不分家的世界长这样:python crawler.py > data.txt——你本想存数据,结果错误信息和进度日志也混进了数据文件,下一次按行解析直接崩掉。分家之后:

数据库课 8-x 讲过【垃圾进垃圾出】,标准流分家就是命令行世界防止【垃圾进】的第一道闸。

四、动手 Demo:亲眼把两条管子分开

写一个两边都说话的程序:

# demo_streams.py
import sys

print("第 1 行正常数据")
print("第 2 行正常数据")
sys.stderr.write("[警告] 网络超时,已重试\n")
sys.stderr.write("[错误] 第 3 条记录格式异常\n")
print("第 3 行正常数据")

在命令行里分别跑三种接法(Windows 的 PowerShell 或 CMD 同样支持 2>):

python demo_streams.py              # 全部混在屏幕
python demo_streams.py 1> out.txt   # stdout 进文件——屏幕上只剩两条 [警告]/[错误]
python demo_streams.py 2> err.txt   # stderr 进文件——屏幕上只剩三行数据

看到什么算成功:跑 1> out.txt 后屏幕上只出现两条诊断信息,而 out.txt 里是干净的三行数据——两条管子当场分开。

五、30 秒实验

跑一条管道命令:

python demo_streams.py | findstr 数据

(macOS/Linux 用 grep 数据。)你会看到【警告/错误】信息仍然漏在屏幕上——因为管道只接走 1 号管(stdout),2 号管(stderr)的输出不进管道。30 秒看懂【为什么日志不会污染下游数据】的机制。

六、常见误区

  1. 【stderr 是【更高级】的输出,优先级更高】——错。它与 stdout 完全平级,只是用途不同(诊断 vs 结果);编号 0/1/2 只是历史惯例的文件描述符。
  2. 【print 出错时自动写 stderr】——不准确。Python 的 print 永远写 stdout;未捕获异常的报错栈才走 stderr。
  3. 【重定向 > 会把内容追加到文件尾】——错。> 是覆盖;追加要用 >>。覆盖掉重要文件时它不经过回收站——树莓派课 3-5 讲过 rm 无回收站,> 同理。
自测三问(点开对答案) 1. **三条标准流的编号、默认去向与分工?** 答:0 标准输入(键盘,程序要读的数据);1 标准输出(屏幕,正常结果);2 标准错误(屏幕,错误与日志)。 2. **`python a.py 2> err.txt` 之后屏幕上还会看到什么?** 答:只剩 stdout 的内容(正常结果);诊断信息全部进了 err.txt。 3. **为什么管道 `|` 接不走 stderr?这带来什么好处?** 答:管道只连接上游的 1 号管与下游的 0 号管;好处是下游拿到的永远是干净数据,日志与警告不会混进去。

相关笔记