重定向和管道
stdin/stdout/stderr 与 重定向
什么是终端设备
人在操作电脑时,其实是和 运行着的程序(术语称之为:进程) 在打交道(术语称之为:交互)。
进程 是 看不见 摸不着的,它们是 在 电脑CPU中执行的代码指令。
所以,我们 和 进程 交互的时候,必须有个设备,我们才能通过它 输入信息给进程,并且显示进程输出信息。
在上世纪六七十年代,计算机网络还没有出现, 计算机都是大型机器,非常的昂贵。 通常只有大公司和研究机构才有。
那时人们通过终端设备和计算机上的程序进行交互,见下图。
注意,终端设备 只有 显示器 和键盘 , 是没有 主机部分的。它和主机之间有根信号线,传递用户通过键盘输入的信息给程序,并显示程序输出的信息。
这样的终端设备,显然不能离主机太远。
人们需要在家、甚至在另外一个城市 操作远程主机,就不能使用这样的终端。
后来有了计算机网络,有了微型个人电脑,我们可以使用个人电脑,运行软件模拟终端设备,比如 putty,进行远程登录主机,访问主机上的进程。
我们在模拟终端程序putty中输入信息,查看putty程序窗口输出的内容。
这种 模拟终端的软件,对Linux来说,也是终端设备。
Linux进程在启动后,通常就会打开3个文件句柄,标准输入文件(stdin),标准输出文件(stdout)和 标准错误文件(stderr)。
Linux进程,要从用户那里读入输入的信息,就是从stdin文件里面读取信息,要 输出 信息 给用户看 都是 输出到 stdout, 要 输出 错误提示 给用户看 都是 输出到 stderr。
而缺省情况下这三个文件stdin、stdout、stderr 都指向 —— 终端设备。
也就是说:
Linux进程从stdin里面读取信息其实就是从终端设备(比如终端模拟程序Putty)读取信息;
Linux进程写入信息到stdout或者stderr,其实就是打印到终端设备上。
如下图所示
比如,运行下面的Python程序,要读入信息,就是从终端读入,因为stdin指向终端设备
再比如:下面的命令echo输出给用户看的,就是输入到终端上,因为stdout指向终端设备
Stdout、Stderr重定向
如果我们在Shell中输入命令的时候,使用 >
符号, 就可以将输入信息输出到其他文件(包括设备文件)中去。比如
ps > out
运行后,我们会发现out文件里面出现了ps的输入信息,而Putty终端窗口里面则没有任何内容打印出来了。
这个 >
就是 stdout 重定向符号, 它表示 stdout 不是指向 终端设备了,而是 重定向到 out 文件。 所以stdout 指向了 out 文件, 输入的信息就到 out
文件了。 终端屏幕上就没有信息了。
这时对应的示意图如下
而Stderr的重定向符号 是 2>
。 注意 2 和 > 之间不能有空格。
比如我们执行下面的命令,其中hhhh是个不存在的文件
我们就会发现putty屏幕上没有任何信息,而文件 err里面则有。
如果我们要,同时重定向stdout和stderr到同一个文件both中,命令写法如下:
如果我们要,重定向stdout到out文件,并重定向stderr到err文件,命令写法如下:
您需要高效学习,找工作? 点击咨询 报名实战班
点击查看学员就业情况
stdin重定向
我们也可以在命令中,将 标准输入stdin 重定向,使用符号 <
用 vi 创建一个 Python 代码文件 add.py
,其内容如下
该程序从stdin 读取一个数字后,显示其加1后的结果。
再创建一个文件 add.dat内容如下(注意3后面有个空行)
执行如下命令
看到了吗?不需要我们从终端输入数字,该程序直接从文件 add.dat中读取数据并执行操作了。
这个 <
就是 stdin 重定向符号, 它表示 stdin 不是指向 终端设备了,而是 重定向到 add.dat 文件。 所以 stdin 指向了 add.dat 文件, 程序就从add.dat 文件读入信息 了。
这时该进程对应示意图如下:
管道
上面我们曾经学习过grep命令,这个命令可以从文件中过滤出 包含指定字符串模式
的行。
比如我们有个文件file1,里面有的行包含了mike 这个词。如果我们想把所有包含mike的行都找出来。 可以执行命令 grep mike file1
。
当grep命令中没有文件参数的时候比如 grep mike, 它就会等待我们在标准输入(一般是putty终端设备)中输入一行行的内容,进行实时的过滤
在Linux操作过程中,我们经常需要 将一个命令的输出的内容,给另一个命令作为输入的内容 进行处理。
比如,我们想查出进程号是 6536 的进程的信息。
我们用ps -ef 可以显示出所有的进程信息,但是这里面的内容太多了,我想过滤出其中包含 6536 字符串的行。
当然可以 用重定向符号 ps –ef > info.txt
, 然后再使用grep从 info.txt中过滤 grep 6536 info.txt
但是这样比较麻烦,我们可以使用 管道操作符
。
我们看 这个命令 ps –ef | grep 6536
注意其中的 竖线 | , 这个就是管道操作符,它起的作用就是
● 将 前面的 ps –ef 命令的stdout(本来是输出到终端设备的) 重定向到一个 临时管道设备里面,
● 同时 将后一个命令 grep 6536 的stdin重定向到这个临时的管道设备。
那么这时会发生什么事情呢?ps –ef 命令的结果直接被 命令 grep 6536
过滤出来了。
这个过程可以用如下示意图表示