

重定向的时候shell在干什么?
source link: https://liutos.github.io/2020/05/22/重定向的时候shell在干什么?/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

“究竟在干什么”是一系列关于软件背后运作原理的文章,每一篇文章旨在讲解一些在日常编程实践中常见但可能并不为人所熟知的技术细节,抛砖引玉,期待激发读者朋友的更多思考。
序言
每当需要 ssh
登录到服务器并运行一个比较花时间的脚本时(比如临时从生产环境导出数据),为了能够知道脚本是否运行结束,或者是否出错退出,我都会将脚本的输出内容重定向到文件中
node foobar.js > /tmp/foobar.log 2> /tmp/foobar.err
如果不在乎将正常的打印和错误混在一起,可以写成
node foobar.js > /tmp/foobar.log 2>&1
上面代码中的 2
和 1
分别是标准错误(C语言中的 stderr
)和标准输出(C语言中的 stdout
)的文件描述符, 2>&1
的意思便是将打印到标准错误中的内容 转移 到标准输出中去——这个 转移 在shell中的术语便叫做重定向(redirection)。
2>&1
该放哪里?
bash
的 man
文档中有一个名为 REDIRECTION
的章节专门介绍了重定向相关的内容,其中有一段有意思的内容
用 ls
不方便做演示,我准备了下面这一段Node.js代码
console.error('Print to standard error.'); console.log('Print to standard output.');
将代码保存到文件 foobar.js
中。
如果将 2>&1
写在后面,那么 foobar.log
中会包含两行
➜ /tmp node foobar.js > /tmp/foobar.log 2>&1 ➜ /tmp cat /tmp/foobar.log Print to standard error. Print to standard output.
否则, foobar.log
中只含有一行内容,另一行会出现在终端上
➜ /tmp node foobar.js 2>&1 > /tmp/foobar.log Print to standard error. ➜ /tmp cat /tmp/foobar.log Print to standard output.
那么为什么会这样呢?
重定向的时候,shell在做些什么?
以执行 node foobar.js > /tmp/foobar.log
为例,当shell发现命令中含有重定向的符号时,便开始忙碌起来。
shell首先用 open
函数打开文件 /tmp/foobar.log
,拿到一个文件描述符(一个非负整数)。Node.js的 fs
模块中有一个 open
方法,在调用成功时,也是往回调函数传入文件描述符
const fs = require('fs'); fs.open('/tmp/cuckoo.log', function (err, fd) { console.log(`fd for cuckoo.log is ${fd}`); fs.open('/tmp/cuckoo.err', function (err, fd) { console.log(`fd for cuckoo.err is ${fd}`); }); });
比较奇妙的是,多次运行时拿到的文件描述符总是相同的
➜ /tmp date; node open.test.js Fri May 22 21:00:56 CST 2020 fd for cuckoo.log is 21 fd for cuckoo.err is 24 ➜ /tmp date; node open.test.js Fri May 22 21:00:59 CST 2020 fd for cuckoo.log is 21 fd for cuckoo.err is 24
说回重定向。shell拿到文件描述符后,便调用 dup2
函数。既然有 dup2
,那么就有 dup
。 dup
接收一个文件描述符作为参数,返回一个新的文件描述符。而 dup2
则接收两个参数,它可以作为让第二个参数的数字成为一个新的文件描述符,指向与第一个参数相同的文件。
用图形可以更好地表达 dup2
的实现原理。下图是一个进程没有重定向时的状态,每个文件描述符都指向它们 原本 对应的文件
作为数字的文件描述符,相当于是 文件描述符表 的数组下标。调用 dup2
后,就变成了
可以将 dup2
理解为:把 文件描述符表 的一个元素(以 dup2
的第一个参数作为下标),按位复制到另一个元素中(以 dup2
的第二个参数作为下标)。
这样一来,凡是写往文件描述符 1
的数据,其实都写到了文件 /tmp/foobar.log
中。
所以,如果命令中重定向操作是 2>&1 > /tmp/foobar.log
,那么文件描述符表中下标1和2的元素并不会指向相同的文件
如果重定向操作是 > /tmp/foobar.log 2>&1
,则如下图所示
因此,此时不管是写往文件描述符1还是2,最终都 重定向 到了 /tmp/foobar.log
中。
后记
如果想要严谨地知道 bash
是如何处理重定向的,可以在GitHub的这个 Bash源代码镜像 上直接查看,找到根目录下的 redir.c 文件即可。
此外,对于上面的示意图,维基百科的 File descriptor词条 也有一幅更严谨的版本。
Recommend
-
158
English|中文简体
-
85
1,使用户HTTP访问自动重定向为HTTPS,直接在http.conf最后配置即可,在httpd.conf文件尾加入如下内容:RewriteEngineonRewriteCond%{SERVER_PORT}!^443$RewriteRule^/?(.*)$[L,R]2,配置域名<EnginedefaultHost="localhost"name="Catalin
-
51
数据重定向这一节内容相对比较简单。 首先理解一下linux的标准输入与输出: 类型 名称 文件描述符 说明 stdin 标准输入 0 一般是键盘输入 stdout 标准输出 1 命令执行成功后的正确信息 stderr 标准错误输出 2 命令执行失败后的错误信息 假如我们不需...
-
104
通配符及输入输出重定向
-
60
什么是shell
-
38
程序员 - @clockwork1122 - 上班屏幕看久眼睛酸很难受,根本不能专心下来。大佬们有什么缓解疲劳的吗?
-
44
当机会来临的时候,你在干什么? - 来来来,大家都来说说,今天都干啥了,是站在广场继续喷看空看多的口水,还是下场实操了? 大家千万别抬轿,我尾盘就割肉。
-
21
-
10
主页 › 当我在做Mod的时候实际在干什么 当我在做Mod的时候实际在干什么 本站内容版权属于本人。转载须告知本人,写明出处,并在文首提供指向本站对应文章的链接。...
-
2
Shell中逐行读取重定向输入发表于2020-11-02|更新于2020-11-02|it在Linux中使用shell处理多行的重定向输入,可以使用以下几种处理方法: 使用for的...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK