news 2026/3/11 4:22:18

第15章 标准IO:重定向和管道

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
第15章 标准IO:重定向和管道

从一开始,Unix 命令行就具备一些特殊的东西,使其区别与其他操作系统,即所谓的的 Unix工具箱:每种 Unix 和 Linux 系统都拥有的大量程序。本章将解释 Unix 工具箱之后隐藏的设计准则,然后师范如何将基本的构建块组合成适合于自己的功能强大的工具。

Unix 设计准则

每个程序都应该是一个单独的工具,或许还有几个基本的选项。一个程序应该只做一件事,但必须出色地完成这个事情。四需要执行一项复杂的任务,应该通过对现有工具的组合来完成(当存在可能性时),而不是再去编写一个新程序。

more 程序(输出显示工具):每次一屏地显示数据,按键就会显示下一屏幕数据;按q键就可以退出。

因此,Unix 的设计准则可以用两句话概括:

  • 每个程序或者命令应该是一个工具,它只完成一件事情,但一定要完成好这件事情;
  • 当需要新工具时,最好对现有的工具进行组合,而不是编写一个新工具;

有时候将这一设计准则描述为:

  • “Small is beautiful(小的就是完美的)” 或者 “Less is more(少的就更好)”;

Unix 新设计准则

CLI(命令行界面)是基于文本的,它无法处理图形和图像,或者没有包含存文本的文件。大多数命令行程序读取和写入文本,这就是为什么这样的程序可以继承在一起工作的原因:他们都使用相同类型的数据。但是当希望处理非文本数据时,必须使用其他类型的程序,所以需要学习 CLI 和 GUI。

常用屏幕显示工具:

  • more:使用简单,所有系统上都有这个程序
  • pg
  • less:许多系统上,less 是默认的程序,而且 less 程序更加出色

Unix 新设计准则:

  • “除非程序无法更小,否则小的就是完美的”

标准输入、标准输出和标准错误

Unix 的开发人员设计了一种读取数据的通用方法(称之为标准输入)和两种写出数据的通用方法(称之为标准输出和标准错误),综合起来,我们称这些功能为标准I/O(standard I/O)。

重定向标准输出

在登录时,shell 会自动地将标准输入设置为键盘,将标准输出和标准错误设置为屏幕。这意味着,默认情况下,大多数程序从键盘读取输入,并将输出写入到屏幕。

但是,每次输入命令时,都可以告诉 shell 在此命令执行期间重置标准输入、标准输出或标准错误。

如果希望命令的输出写到屏幕上,则无需做任何事情,这是自动的。

如果想将命令的输出写入到文件中,需要在命令后面键入 >(大于号)字符,后面跟文件名:

sort>names# 该命令将它的输出写入到一个叫 names 的文件中

写入到的目的文件可以存在,也可以不存在。如果不存在,shell 将自动创建这个文件;如果存在,那么它的内容将被替换。如果希望将新数据添加到原有的数据后面,需要用 >> 字符,即两个连续的大于号字符:

sort>>names# 将新数据追加在已有文件的尾部

将标准输出发送给文件时,即为重定向标准输出。

如果希望 shell 永远不替换现有文件的内容,可以通过设置 noclobber 选项(Bash、Korn shell)或者 shell 变量 noclobber(C-Shell、Tcsh)。

set-o noclobber

如果设置了此选项,并且重定向标准输出到一个已经存在的文件,那么 Bash 将拒绝执行命令,并给出如下消息:

bash: names: cannot overwrite existing file

重定向标准输入

默认情况下,标准输入被设置成键盘。这意味着,当运行需要读取数据的程序时,程序期望用户通过键盘输入数据,每次一行。但结束输入数据时,可以按 ^D(组合键)发送 eof 信号。

重定向标准输入,使命令从文件中读取数据而不是键盘,只需要在命令的最后键入 <(小于号),后面跟着文件的名称即可:

sort<names# 从 names 文件中读取内容sort</etc/passwd# 从 /etc/passwd 文件中读取内容

标准输入和标准输出可以同时重定向:

sort<rawdata>report

重定向标准错误、文件描述符

shell 提供两种不同的输出目标:标准输出和标准错误。标准输出用于正常输出,标准错误用于错误消息。两者皆可重定向。但重定向标准错误的语法不相同。

在 Unix 进程中,每个输入源和每个输出目标都由一个唯一的数字标识,这个数字称为文件描述符(file descriptor)。

文件描述符的本质代表着进程对 I/O 资源的引用。在 Linux/Unix 系统中,文件描述符是一个非负整数,它代表了进程与一个文件、管道、套接字(Socket)或其他 I/O 资源之间的连接。关注如下的命令:

calculate8>results

此命令在执行之前,shell 解析后会为这个进程创建值为8的文件描述符,并使其与 results 文件关联。其中的 > 表明这个文件是用于写入的,即打开文件的模式是只读还是只写的,另外,如果要创建一个可读可写的文件描述符,可以使用 <>:

calculate8<>results

在 Bourne shell 家族中,重定向输入或输出的正式语法是在文件描述符数字之后使用 <(小于号)或 >(大于号)。默认情况下,Unix 为每个进程提供3个预定义的文件描述符:0 代表标准输入;1 代表标准输出;2 代表标准错误;

因此,重定向标准IO 的写法如下:

command0<inputfile# 重定向标准输入,同 command < inputfilecommand1>outputfile# 重定向标准输出,同 command > outputfilecommand2>outputfile# 重定向标准错误

为了方便起见,如果重定向输入时省略了 0,那么 shell 假定指的就是标准输入;如果重定向输入时省略 1,那么 shell 假定指的就是标准输出。

同一个条命令可以指定多个重定向:

sort0<rawdata1>results2>errorssort<rawdata>results2>errors

以上重定向只适用于 Bourne shell,在 C-Shell 家族中重定向标准错误比较复杂。

子 shell

进程就是加载到内存中并且准备运行的程序,以及程序的数据和跟踪程序所需的信息。当进程需要启动另一个进程时,这个进程就创建一个副本进程。原始的进程称为父进程,副本进程称为子进程。

子进程开始运行后,父进程等待子进程死亡(也就是结束)。一旦子进程死亡,父进程就会被唤醒,重新获得控制权并再次开始运行,此时子进程消失。

shell 在解析命令时,判断命令是内部命令还是外部命令,内部命令 shell 就会在自己的进程中直接解释执行。而外部命令 shell 需要查找合适的程序,然后以一个新进程运行这个程序,当该程序终止时,shell 重新获得控制权,此时shell 就是父进程,shell 运行的程序就是子进程。

当一个 shell 启动另一个 shell 时,我们称第二个 shell 为 子shell。当创建子 shell 时,它集成父shell 的环境,但是,子shell 对环境的任何改变都不会传递回父shell。

除了直接输入命令启动一个新的 shell,还有一个方法让命令在 子shell 中运行:将命令括在圆括号中。这样就可以告诉 shell 在子shell 中运行命令:

(date)# 在子shell 中运行 date 命令

重定向标准输出和标准错误到同一个文件

对于 Bourne Shell 其基本思想就是将一种类型的输出重定向到一个文件,然后再将另一种类型的输出重定向到同一个位置:

commandx>outputfile y>&xsort1>output2>&1# 重定向标准输出和标准错误到文件 outputsort&>output# bash 4.0 以上,可以使用此写法,也可以使用 sort >& output# 错误!在一条命令中重定向同一个文件两次的话,一个重定向会覆盖另一个重定向sort>output2>output

其中 &1 标识文件描述符1当前指向的地址。

抛弃输出

当需要抛弃输出时,只需要重定向输出到一个特殊的文件:/dev/null;对于此文件,发送给它的任何东西都会永远消失。

另外,特殊文件 /dev/tty 表示终端,数据发送给次文件时,输出就显示在显示器上。

Bourne Shell 家族的重定向总结

这里只提供 Bourne shell 家族的重定向总结,C-Shell 不再给出。以下为 Bourne shell 家族的标准IO的重定向:

元字符动作
<重定向标准输入(同 0<)
>重定向标准输出(同 1>)
>|重定向标准输出:强制重写
>>追加标准输出(同 1>>)
2>重定向标准错误
2>>追加标准错误
2>&1(原书有错误)将标准错误重定向到标准输出
>& 或者 &>重定向标准输出 + 标准错误(只适用于 bash)
|将标准输出通过管道传送给另一条命令
2>&1 |将标准输出+标准错误通过管道传送给另一条命令

大多数命令行程序使用标准IO进行输入和输出,输入来源于标准输入(stdin)、正常输出发送到标准输出(stdout)、错误消息则发送给标准错误(stderr)。

对于 Bourne shell 家族,可以使用文件描述符(stdin=0、stdout=1,stderr=2)及各种元字符控制标准IO。当没有歧义性时,可以省略文件描述符,为了防止不小心重写了已有的文件,可以设置 shell 选项 noclobber。如果设置了 noclobber 选项,则可以使用 >| 强制进行重写。

  • ls(list,列举)命令显示文件的信息,使用 -l(long)选项,ls可以显示文件的信息。
  • touch 命令可以用来创建一个空文件。
  • cat 命令可以用于查看某个文件的内容。
  • rm(remove,移除)命令用于删除文件。

管道线

Unix 开发人员的目标就是构建小的工具,每个工具只做一件事情,当一个工具无法解决的问题时,能够使用一组工具来完成这个任务。

例如,假设有3个文件,每个文件中的每一行都是一个人的信息,现在要找出有多少人叫 Harley。

首先,使用 cat(catenate,连接)命令将这几个文件组合在一起;然后使用 grep 命令抽取所有包含单词 Harley 的行;最后,使用 wc(word count,单词统计)命令和 -l(line count,行统计)选项统计行的数量,即:

catfile1 file2 file3>tempfile1# combine filesgrepHarley<tempfile1>rempfile2# extract lineswc-l<tempfile2# count linesrmtempfile1 tempfile2# delete temp files

注意,这些命令使用重定向在临时文件中存储中间结果,当工作完成时再将临时文件删除。
上面的命令序列可以完成任务,但是有一个缺陷:将所有事情连接在一起粘合剂是使用临时文件进行重定向,这种方式难以理解,且复杂。

为了使这样的解决方法简单些,shell 允许创建一序列命令,在这一序列命令中,一个程序的标准输出可以自动地发送给下一个程序的标准输入。当这样做时,两个程序之间的连接就是管道(pipe),而命令序列本身称为管道线(pipeline)。创建管道线,只需要将命令之间使用 | 字符(管道符号)分隔开即可。

例如,上面的命令序列可以使用下列命令替代:

catfile1 file2 file3|grepHarley|wc-l

在 Unix/Linux 命令行中,管道符(|)的作用是将一个命令的标准输出(Standard Output, stdout)连接到另一个命令的标准输入(Standard Input, stdin)。在上面的命令中,三个程序是在各自独立的进程中执行的。

对于 Bourne shell 家族来说,可以将标准输出和标准错误组合在一起,然后一起发送给另一个程序:

command12>&1|command2# command1 和 command2 都是命令

当创建管道线时,必须使用能够从标准输入读取文本,并向标准输出写入文本的程序。我们称这样的程序为 过滤器,许多程序都是过滤器。

实际中,大多数管道线只连续使用2条或者3条命令。到目前为止,管道线最常见的应用就是将一些命令的输出传递给 less,从而可以每次一屏地显示命令的输出:

cal2008|less

管道线分流:tee

有时候,可能希望将程序的输出同时发送到两个地方。例如希望将一个输出保存在文件中,同时还发送到另一个程序。

使用 tee 命令可以实现这目的。tee 命令的作用就是从标准输入读取数据,并向标准输出和一个文件各发送一份数据,其语法为:

tee[-a]file...# file 是希望将数据发送到的文件的名称

例如上面的问题中:

catnames1 names2 names3|grepHarley# 显示三个文件中包含Harley 的所有行# 加入 tee 进行分流catnames1 names2 names3|teemasterlist|grepHarley# 此时 cat 的输出保存在 masterlist 中,同时还将传送给 grep

使用 tee 时,通过指定更多个文件名可以为输出保存不止一份副本:

# 在此管道线中,tee 将 cat 的输出复制到两个文件 d1 和 d2 中catnames1 names2 names3|teed1 d2|grepHarley

如果在 tee 命令中指定的文件不存在,那么 tee 命令会创建这个文件。但是,使用时必须小心,如果文件存在,那么 tee 命令将会重写这个文件,原始内容就会丢失。如果希望 tee 命令在文件的末尾追加数据,而不是替换文件,则可以使用 -a(append,追加)选项,例如:

# 在此管道线中,tee 将 cat 的输出保存在 backup 文件中,如果该文件存在,则追加到文件的末尾catnames1 names2 names3|tee-a backup|grepHarley

注意下面的这个模式:

command|teefile# 将 command 的输出保存在文件中并输出到屏幕上who|teestatus# 将 who 的输出保存在 status 文件中并显示在屏幕上

在此模式下,不必在 tee 之后使用另一个程序。因为 tee 将它的输出发送给标准输出,默认情况下就是屏幕。

管道线的重要性

实际上,管道线的实现是 Unix 设计准则提升的催化剂。在很大程序上,正式管道线和标准IO使Unix 的命令行界面功能如此强大。

条件执行

有时候,希望在前一条命令成功执行的条件下执行另一条命令,语法为:

command1&&command2

有时候,希望在前一条命令没有成功执行的条件下执行另一条命令,语法为:

command1||command2

这里就是当前一条命令成功执行或失败时才执行另一条命令,这就是所谓的条件执行。
观察如下命令:

# 查找 people 文件中的 Harley 行,如果存在,就对 people 内容进行排序并输出到 contacts 文件中grepHarley people&&sortpeople>contacts# 与上一个命令相同,不同的是此命令不显示 grep 的输出grepHarley people>/dev/null&&sortpeople>contacts# 如果 update 命令执行失败,就显示提示update|echo"The update program failed."

命令的成功或失败由其退出状态码决定,0 表示成功,非 0 表示失败。command1 && command2 依赖 command1 的退出码为 0 来决定是否执行 command2。你可以通过 echo $? 检查上一个命令的退出码,或参考命令的文档了解具体退出码含义。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/7 4:20:06

广东省考备考三要素(喻明公考)

材料结构化面试备考要注意的方面有很多&#xff0c;但是抓住关键才能有效备考。在实际的学习中&#xff0c;喻明公考提醒各位考生有三个备考的关键要素需要深入把握。一、学好普通结构化材料结构化归根到底还是在普通结构化的基础上进行的创新&#xff0c;背景材料对作答有指导…

作者头像 李华
网站建设 2026/3/3 17:40:23

好写作AI|学术萌新救星:你的“论文私教”如何把知识喂到嘴边

写论文像在迷宫里裸奔&#xff1f;别慌&#xff01;你的“学术导航仪”已上线各位刚踏入学术圈的萌新们&#xff0c;是不是觉得论文写作就像突然被扔进深海却没人教游泳&#xff1f;面对“文献综述”、“研究方法”这些专业术语一脸懵&#xff1f;别怕&#xff01;今天给你们安…

作者头像 李华
网站建设 2026/3/10 6:43:11

【紧急预警】环境指标悄然变化!用R语言快速识别趋势拐点的方法

第一章&#xff1a;环境监测的 R 语言趋势检验在环境科学领域&#xff0c;长期监测数据的趋势分析对于评估气候变化、污染水平演变及生态响应至关重要。R 语言凭借其强大的统计建模与可视化能力&#xff0c;成为执行环境趋势检验的首选工具。常用方法包括Mann-Kendall非参数趋势…

作者头像 李华
网站建设 2026/3/3 23:20:45

揭秘高产农田背后的算法秘密:R语言如何改变传统农业决策

第一章&#xff1a;农业产量的 R 语言种植建议模型在现代农业数据分析中&#xff0c;R 语言因其强大的统计建模与可视化能力&#xff0c;被广泛应用于作物产量预测和种植策略优化。通过整合气象数据、土壤特征与历史收成记录&#xff0c;可构建一个基于回归分析与机器学习的种植…

作者头像 李华
网站建设 2026/3/5 14:18:25

R语言在量子计算中的实战应用(多qubit扩展全解析)

第一章&#xff1a;R语言量子计算环境搭建与多qubit基础在现代计算科学中&#xff0c;量子计算因其对复杂问题的潜在加速能力而备受关注。R语言虽以统计分析见长&#xff0c;但通过特定扩展包可支持量子计算模拟&#xff0c;为研究者提供从数据分析到量子算法验证的一体化环境。…

作者头像 李华
网站建设 2026/3/7 7:29:35

紧急修复检索偏差!:Dify重排序参数调优的4步快速响应方案

第一章&#xff1a;紧急修复检索偏差&#xff1a;Dify重排序参数调优的4步快速响应方案在使用 Dify 构建检索增强生成&#xff08;RAG&#xff09;应用时&#xff0c;检索结果的准确性直接影响最终输出质量。当出现检索偏差——即相关文档未能被有效排序至前列时&#xff0c;需…

作者头像 李华