聊到linux,重定向绝对是最常被低估的一个技能。很多人一开始只是把它当成“把命令输出存到文件里”的符号,反正 > 就是覆盖写, >> 就是追加写。但等到真正写脚本、部署服务、排查日志的时候才发现,那些“莫名其妙”的问题——为什么日志没生成、为什么命令悄悄失败、为什么管道里的数据丢了——十有八九都能回溯到对重定向理解不透。
这篇文章我想把重定向这件事彻底讲透。不是单纯罗列符号用法,而是从“为什么需要重定向”开始,把它的底层逻辑、常见操作符的细微差别、和管道之间的协作关系、以及日常运维里踩过的坑全部梳理一遍。适合的对象包括:刚学完linux基础命令的初学者、写shell脚本经常出bug的开发者、以及想在服务器上规范管理日志的运维同学。读完你至少能搞清楚一个经典句式 > file 2>&1 为什么必须这么写,也能在遇到异常输出时快速定位是哪里出了问题。
1. 标准输入输出与文件描述符:重定向的基础逻辑
1.1 文件描述符0、1、2到底是什么
要理解重定向,先得知道linux进程是怎么看待“输入输出”的。简单说,每个进程启动的时候,内核会为它维护一张文件描述符表,这张表里记录着这个进程可以访问哪些文件、设备或者管道。表中的每一项用一个非负整数表示,也就是文件描述符(fd)。
这里面有三个编号是约定俗成的:
0是标准输入(stdin),默认对应键盘;1是标准输出(stdout),默认对应当前终端屏幕;2是标准错误(stderr),默认也对应当前终端屏幕。
我给你打个比方。可以把进程想象成一家饭店的后厨,文件描述符就是后厨和外界沟通的三条通道:0号通道是食材入口,1号通道是出菜口,2号通道是垃圾和投诉出口。正常情况下,食材从采购入口进,菜品从出菜口端给客人,投诉意见也从另一个通道报给经理。重定向干的事,就是把这几个通道重新接一下:不等客人来点菜,直接把菜单从文件里读;不把菜端给客人,直接扔进垃圾桶;投诉不报给经理了,直接写进一个本子里。
在linux下用 ls -l /proc/self/fd/ 就能看到当前进程的文件描述符情况,里面会有 0 、 1 、 2 这三个符号链接指向各自的设备文件。这个目录是理解文件描述符的绝佳观察窗口,后面排查问题也会用到。
1.2 为什么需要重定向:默认流向的局限性
默认情况下,命令的输入来自键盘,输出到屏幕。这在交互式操作时没问题,但一旦进入自动化场景就尴尬了。
比如你写了一个脚本 backup.sh ,里面有一堆命令。如果所有输出都往屏幕上怼,终端会瞬间被刷爆,而且你根本来不及看哪些步骤失败、哪些步骤成功。更麻烦的是,如果脚本被 cron 定时任务调度执行,它的输入输出默认是不关联终端的——没有重定向的话,标准输出和标准错误根本不知道往哪里输,很多情况下会通过邮件发给系统账号,或者直接丢失。
这时候你就需要把1号通道(stdout)接到一个日志文件上,把2号通道(stderr)也接过去,脚本跑完再统一查看日志。这就是重定向存在的基本意义:把进程的io流从“默认位置”改变到“你想要的位置”。
另外一个隐藏但同样重要的点是:标准输出和标准错误是分开的两个通道。为什么要分开?因为你要区分“正常结果”和“错误信息”。正常结果可以留存作为业务数据,错误信息需要单独监控。如果两者混在一个通道里,后面做日志分析、异常告警就会非常痛苦。
1.3 重定向的底层机制:shell解析与dup2
很多教程只告诉你符号怎么用,不讲背后的系统调用,导致遇到一些诡异场景时完全无法推理。其实重定向的底层操作非常清晰: shell 在启动命令之前,会先解析你写的重定向部分,然后调用 open() 打开目标文件,再调用 dup2() 把目标文件的文件描述符复制到1号或2号位置,最后才执行命令。
关键点在于这个“先解析再执行”的顺序。 dup2() 的作用是原子性地让一个文件描述符指向另一个描述符所指向的文件表项。比如你写 ls > out.txt ,shell会先 open("out.txt", o_wronly|o_creat|o_trunc) 得到一个fd 3,然后执行 dup2(3, 1) 让fd 1也指向out.txt这个文件,接着关闭fd 3,最后执行 ls 。此时 ls 的输出写进fd 1,自然就进了文件。
理解了这一步,你就明白为什么 > file 会先清空文件——因为 open() 时带了 o_trunc 标志。就算后面的命令执行失败,文件也已经被截断成0字节了。踩过“想备份结果却把原文件清空”这种坑的人,一定对这个机制记忆深刻。
2. 三类重定向操作符的详细拆解
2.1 标准输出重定向:>与>>的选择
> 和 >> 是最常用的两个符号,区别是个老生常谈: > 覆盖写入, >> 追加写入。但实际使用中还是有几个细节值得单独拎出来说。
第一, > 的目标文件不存在时会自动创建,但不会帮你创建上级目录。所以写 > /var/log/myapp/access.log 之前,必须保证 /var/log/myapp/ 目录已经存在,否则会报 no such file or directory 。很多初学者在这里犯迷糊,以为是权限问题,实际是路径问题。
第二, >> 追加的时候不会清空文件,这个没有悬念,但在做日志轮转时要注意,如果你用 cron 把旧日志内容清理掉,而服务还在不停写,文件指针会错位。更规范的做法是让服务重新打开日志文件,或者用 logrotate 的 copytruncate 方案。
第三, > 还有一个隐藏的坑:如果你写 > file 前面没有任何命令,shell会直接创建一个空文件。用这个方式快速初始化一个文件可以,但如果不小心把变量值写错位置,比如 > config.ini 本来想写内容结果给截断了,那就只能自认倒霉。我习惯在需要清空但有价值的配置文件时,先做备份再做截断操作。
顺便说一句, 1> 就是 > 的完整写法,默认省略了数字1。如果你看到别人写 1>log.txt ,和 >log.txt 是等价的。
2.2 标准输入重定向:<、<<与<<<
输出重定向大家用得顺手,输入重定向其实同样重要。 < 的作用是把文件内容作为命令的标准输入。比如 wc -l < access.log 可以统计日志行数,注意它和 wc -l access.log 的区别:前者从stdin读,后者把文件名当参数传给wc。结果一样,但语义不同。用 < 的好处是文件路径不会被程序当作参数,某些程序对参数和stdin的处理逻辑完全不同。
除了 < ,还有两个衍生写法:
<< ,也就是here-document。它可以从脚本里直接嵌入一段多行文本作为输入,常用于给交互式程序提供输入,或者给配置命令喂内容。典型写法是:
cat <<eof > /etc/myapp/config.conf
server {
listen 8080;
root /var/www;
}
eof
这个写法等价于“把eof之间的内容写入config.conf”。注意结尾的 eof 必须顶格写,不能有行首空格(除非你用了 <<- 并配合tab),否则会报错。许多人在写自动化部署脚本时被这个细节坑过。
<<< ,here-string。它是here-document的简化版,直接把单个字符串作为stdin输入。比如 bc <<< "1+2" 会输出3,省去了echo加管道的写法。在需要给某些命令提供参数值时很方便。
输入重定向的真正价值在于:它让你可以绕开“必须通过参数传文件路径”的限制,把内容处理逻辑交给管道和命令本身。比如 while read line; do ...; done < file.txt 就是逐行处理文件的标准姿势,这里面的 < file.txt 就是在给整个循环体提供标准输入。
2.3 标准错误重定向:2>、2>&1与&>
标准错误是第二个通道,默认也输出到屏幕,但问题在于:当你只想保存正常结果时,错误信息还是会哗啦啦地冒出来。反过来,有时候你想单独收集错误日志,也不想让屏幕被正常输出刷屏。这时就要用 2> 。
2>error.log 的意思是把fd 2的输出重定向到error.log文件。 2>>error.log 就是追加形式。这个很简单,难的是 2>&1 。
2>&1 是一个让很多人绕晕的写法。它的字面含义是:把标准错误(fd 2)指向标准输出当前所指向的位置。注意是“当前所指向的位置”,不是“指向标准输出本身”。如果你的命令是 command >all.log 2>&1 ,那么shell先处理 >all.log ,让fd 1指向all.log文件,紧接着处理 2>&1 ,让fd 2也指向all.log,这样stdout和stderr就都写进了同一个文件。
反过来,如果你写 command 2>&1 >all.log ,shell先处理 2>&1 ,此时fd 1还指向终端屏幕,所以fd 2也指向屏幕;接着处理 >all.log ,fd 1转到了文件。最终结果只有stdout进了文件,stderr依旧打印到终端。这个顺序差异是经典考点,也是实际排障中最容易踩的坑之一,我专门在后面“常见问题”章节用实例说明。
&> 是bash提供的一个便捷写法, &>file 相当于 >file 2>&1 ; &>>file 相当于 >>file 2>&1 。写起来短,但可读性稍差,在团队脚本中如果注释不清晰,建议还是用显式写法更稳妥。
2.4 深入2>&1的本质:一次dup操作
既然提到 2>&1 ,我想再多说几句它背后的 dup2 机制。这能帮你在面对更复杂的场景时举一反三。
2>&1 里这个 & 不是“和”的意思,它表示“后面跟的是一个文件描述符,而不是普通文件名”。你写 2>1 会把错误输出写入名为 1 的文件;写 2>&1 才会把fd 2重定向到fd 1所指向的位置。很多新手在初学阶段都会不小心创建一个叫 1 的垃圾文件,原因就在这个 & 上。
dup2(1, 2) 系统调用的效果是:让fd 2和fd 1指向同一个文件表项。这里有个重要细节:fd 2复制的是fd 1“当时的指向”,不是“fd 1这个名字”。所以如果之后fd 1被重新指向别的地方,fd 2不会跟着变。反过来,如果你先让fd 1指向a文件,又让fd 2指向b文件,再想让fd 2回到a,必须再次写 2>&1 ?不,如果此刻fd 1已经指向a,就可以。但如果你先改变fd 1,再写 2>&1 ,fd 2就会跟着新指向走。记住这一点,很多顺序问题都能推导出来。
还有一种进阶玩法是交换fd 1和fd 2,bash里可以写 3>&1 1>&2 2>&3 3>&- 。这在某些需要“让错误信息进普通日志、正常输出进错误日志”的特殊场景中有用,但日常用得少,了解即可。重点是:你理解了它是在操作一张文件描述符表,而不是在操作“输出方向”,很多怪问题就有了合理的解释。
3. 实战案例:日志管理、脚本执行与命令组合
3.1 日志收集的标准姿势:nohup与2>&1的搭配
日常部署后台服务时,最经典的一个命令是:
nohup java -jar app.jar > app.log 2>&1 &
这行命令背后有三层意思: nohup 让进程在终端退出后继续运行, & 让命令在后台执行, >app.log 2>&1 把标准输出和标准错误都收进日志文件。很多人是从同事那里复制这行命令用的,但没想过为什么写 2>&1 而不是只写 >app.log 。
原因很简单:程序运行时的异常堆栈、打印错误信息都是走stderr的,如果你只重定向stdout,stderr还是会打到终端。在 nohup 场景下,没有终端挂载时stderr可能直接丢失,你就看不到任何报错。把两者合并到同一个文件,虽然日志会混在一起,但对于排障来说总比没有好。后续如果嫌日志太乱,可以考虑用 2>error.log 单独存错误,或者借助 tee 分流。
类似的场景还有 cron 。 cron 执行的脚本默认会把输出邮件发给用户,很多服务器上邮件服务甚至没配好,导致输出被丢弃。所以在 crontab 里写的每一条定时任务,建议都显式加上重定向,例如:
*/5 * * * * /opt/scripts/check.sh >> /var/log/check.log 2>&1
这样日志就不会丢,排查定时任务是否正常执行也能有据可查。
3.2 脚本内的全局重定向:exec命令
如果你写了一个脚本,想让里面所有命令的输出都统一进一个日志文件,不用每条命令都手动追加 >>log 2>&1 ,可以在脚本开头用 exec 直接改变当前shell本身的文件描述符:
#!/bin/bash exec >> /var/log/my_script.log 2>&1 echo "script start" date some_command_here
这段代码会让脚本里后续所有命令的stdout和stderr都追加到同一个日志文件。体感上的效果是“当前shell环境里fd 1和fd 2都被换掉了”,因此所有子命令继承到的也是被重定向后的通道。
用 exec 做全局重定向有几个注意点。一是文件名带日期时,建议先在外面算好变量,再传给 exec ;二是如果你在脚本中途想恢复原始的屏幕输出,必须先保存原始fd,比如开头的 exec 3>&1 ,后续想输出到屏幕时用 echo "..." >&3 ;三是注意 exec >file 之后,如果脚本里某个命令需要交互式输入,会很麻烦,因为stdin没变,但输出全进了文件,用户看不到提示。
这个技巧在写自动化构建脚本、部署脚本时非常实用,能把日志集中管理,也方便后续归档。
3.3 丢弃与探测:/dev/null的妙用
/dev/null 是linux里的“黑洞设备”,任何写入它的数据都会被丢弃。最常见的用法是 command >/dev/null 2>&1 ,意思是既不要正常输出也不要错误输出。
这个写法在什么时候用?比如你只想判断一个命令是否执行成功,不关心输出,就可以这样写:
if curl -s -o /dev/null --connect-timeout 5 http://example.com/health; then
echo "service is up"
else
echo "service is down"
fi
-o /dev/null 把响应体丢进黑洞, -s 静默模式隐藏进度信息,这样 curl 的退出码就能用来判断http请求是否成功。同样的思路也适用于 ping 、 mkdir -p 等命令。
但这里想提一个容易忽略的坑: /dev/null 是一个字符设备文件,不是普通文件。频繁向它写入数据时,系统的io调度其实也会产生开销,只是数据被丢弃而已。如果某个程序疯狂输出日志到你用 >/dev/null 重定向的地方,cpu和io仍然会有损耗,所以不能认为“丢进黑洞就绝对无成本”。真正遇到刷屏式日志时,还是应该从应用层面把日志级别调低。
另外提一句, /dev/null 还可以用来“快速清空大文件”:
cat /dev/null > huge_log.log # 或者 : > huge_log.log # 或者 truncate -s 0 huge_log.log
效果都是保留文件占位符、把内容清空为零字节,比 rm 再新建更安全,因为不会改变文件的inode和已有权限属性,运行中的服务写入句柄也不会断。
3.4 文本统计与处理中的输入重定向
输入重定向在日常文本处理中非常好用,但经常被忽略。举几个例子:
# 统计单词数,不显示文件名
wc -w < report.txt
# 从文件读入内容做字典模式搜索
grep -f keyword_list.txt < data.txt
# 用文件内容作为循环输入
while ifs= read -r ip; do
ping -c 1 "$ip" >/dev/null && echo "$ip alive" || echo "$ip dead"
done < ip_list.txt
这里说一下 wc -w < report.txt 和 wc -w report.txt 的差别。前者内容经过stdin流入,wc不会在输出中列出文件名;后者会列出文件名。在脚本里做变量赋值时,前者更方便——你直接 count=$(wc -w < report.txt) 就能得到纯数字,后面不用再处理文件名部分。这种细微差别在你写管道命令、做条件判断时能省掉不少 awk 截取的功夫。
4. 重定向与管道:组合技与常见误区
4.1 管道和重定向的本质区别
管道( | )和重定向( > < )常被放在一起说,但它们的本质完全不同。重定向是让进程的某个文件描述符指向一个文件或设备,管道是连接两个进程,把前一个进程的stdout和后一个进程的stdin接到同一个内存缓冲区上。
一个容易犯的错误是混淆 | 和 > 的使用场景。 command1 | command2 是把输出传给另一个程序处理, command1 > file 是把输出存下来。如果你想“既传给另一个程序,又同时保存”,单靠 | 或 > 都不够,需要 tee 参与。
另一个细节是:管道默认只传递stdout,不传stderr。这就是为什么 command | grep error 抓不到错误信息——因为错误信息在fd 2上,没有进入管道。如果要让grep也能匹配到错误,必须写 command 2>&1 | grep error ,也就是先把fd 2重定向到fd 1,再让fd 1进入管道。
很多人在调试时觉得grep结果“不完整”,往往就是忘了stderr没进管道。这个点结合重定向理解就特别顺:管道的本质是fd 1接到了另一个进程的fd 0,与fd 2无关;想让stderr一起走,就需要先让fd 2“复制”fd 1的目标,也就是 2>&1 。
4.2tee:一鱼多吃
上面提到 tee ,它的标准用法是“从stdin读入,同时写入文件和stdout”。最常见的例子:
some_command | tee output.log
这条命令会看到命令输出实时打印在屏幕上,同时完整写入output.log。比单纯重定向更友好,尤其适合那种耗时很长的构建过程——你既想在终端盯着进度,又想留下完整日志。
如果还想追加而不是覆盖,用 tee -a 。如果想在写入文件时顺便做点其他处理,还能再接管道:
some_command | tee output.log | grep error
这个链路的含义是:some_command的输出先被tee接住,同时分流到文件output.log和下游的grep。实际运维中经常用它来“归档+实时过滤”。
还有一个小技巧:需要root权限写文件时, > 会因为权限问题失败,但 tee 可以结合sudo绕过这个限制。比如:
echo "options" | sudo tee /etc/sysctl.d/99-tuning.conf >/dev/null
注意最后的 >/dev/null 是把tee自身打印到屏幕的那份输出丢进黑洞,因为你要的是“只改文件,不刷屏”。这个写法比 sudo sh -c 'echo ... > file' 更清晰,也不容易引号嵌套出错。
4.3 进程替换:重定向的“匿名管道”版本
bash有一种比管道更灵活的机制叫进程替换,语法是 <(command) 和 >(command) 。它本质上是为命令创建一个临时文件描述符,把另一个进程的输出/输入接到上面,常用于需要“文件参数”的大量工具。
一个典型例子是 diff 比较两个命令的输出,而不是两个文件:
diff <(ls dir1) <(ls dir2)
这个写法把两个 ls 的结果分别放进两个临时fd,diff把它们当成两个文件来比较,免去了手动生成临时文件的麻烦。
进程替换在某些场景下比管道更胜一筹,因为它不需要改变标准输入输出的语义,命令可以用正常方式读取“文件”。比如 while read line; do ...; done < <(grep pattern file) 就很自然地在循环里用了命令输出作为输入源。
这里要严格区分进程替换不是管道,它的执行是异步的,命令结束后临时fd会被清理。理解不了这个细节没关系,只要记得“进程替换适合把命令输出当文件用的场合”就够了。
4.4 组合实战:日志实时监控与筛选
综合上面的工具,来写一个实际场景:监控一个名为 app.log 的日志文件,看最近是否有“error”,同时把完整日志归档。
tail -f app.log | tee -a /tmp/app_archive.log | grep --line-buffered error
这里 tail -f 持续输出新增日志, tee -a 把内容追加进归档文件, grep --line-buffered error 实时过滤出错误行。 --line-buffered 让grep在拿到一行后立即输出,而不是攒满缓冲区,这样终端能看到“实时”的错误流。
类似的组合在排查线上问题时非常常用。但要注意,管道链中的每一个命令都有自己的缓冲策略。 grep 在非tty输出时默认使用块缓冲,如果不加 --line-buffered ,你会觉得日志“卡住”了,实际上是被缓冲区攒着没输出。这也是管道使用中一个极容易被忽视的体验问题。
5. 常见坑与排查技巧:从现象反推原因
5.1 顺序很重要:2>&1 >file与>file 2>&1
这是重定向里最经典的坑,没有之一。我见过很多同学背下了 >file 2>&1 这个写法,但不知道其中的顺序逻辑。当有人反着写,结果就翻车了:
# 错误示范 command 2>&1 >file
前面解释过机制: 2>&1 先执行,此时fd 1还指向终端屏幕,所以fd 2也被指向屏幕;接着 >file 让fd 1指向文件。最终stdout进文件,stderr留在屏幕。大部分人的直觉是“我明明让错误也走1了,为什么还是打屏?”——因为“走1”发生在“1还没变成文件”的时候。
排查这类现象有个经验:看到“stderr没有进文件”,先检查 2>&1 是不是写在 > 前面了。如果换行、换序后正常,那就不是命令本身的问题,而是shell解析重定向的顺序问题。
5.2 权限问题与sudo:重定向不生效的秘密
有一个高频场景:普通用户想写一个需要root权限的文件,于是写出这样的命令:
sudo echo "some config" > /etc/systemd/system/myapp.service
结果shell报 permission denied 。为什么?因为重定向是当前shell做的,不是sudo做的。当前shell尝试以当前用户权限打开 /etc/systemd/system/myapp.service ,自然被拒绝;sudo只作用于echo命令本身,它输出的内容是交给shell去写文件的,但shell没有root权限。
解法用 tee ,就是我前面提到的:
echo "some config" | sudo tee /etc/systemd/system/myapp.service >/dev/null
tee 以root身份运行,它来打开目标文件并写入,权限就对了。同理,需要向 /proc/sys 等内核参数写入时,也建议用这个模式。
5.3 here-document的坑:eof顶格与tab
写自动化脚本时here-document用得不少,典型的坑有两个。
第一个是结束标记必须顶格。如果你在脚本里这样写:
cat <<eof > /tmp/test.txt hello eof
bad。因为 eof 前面有两个空格,shell会认为文本内容还没结束,于是后面的命令全被当成内容灌进文件,脚本逻辑全部乱套。解决办法是保证 eof 从行首开始,或者使用 <<- 标记并允许制表符开头:
cat <<-eof > /tmp/test.txt hello eof
注意这里 <<- 只忽略行首的tab字符,空格无效,所以写脚本时如果想缩进,必须用tab键而不是空格键。这个细节在团队协作时经常引发无意义的“玄学bug”,非常值得警惕。
第二个坑是变量展开。默认情况下here-document中的 $变量 会被展开。如果你希望内容里的 $ 保持原样(比如写一个包含环境变量引用的配置模板),需要把结束标记用引号包起来:
cat <<'eof' > /tmp/config.tpl
user=${user}
path=/custom/bin:$path
eof
这里单引号包裹的 'eof' 让整段内容不做变量展开,所有 $ 都会原样写入文件。理解这一点,你写配置文件模板时就不会出现变量被意外替换成空串的问题。
5.4 为什么重定向后屏幕还有输出
如果你明明写了 command > file ,屏幕还是能看到内容,不要怀疑shell出bug了,先想一想这些内容来自哪个通道。最常见的答案是:来自stderr。 > 只重定了fd 1,fd 2还是原样输出到终端。
解决思路有两个:一是 command >file 2>&1 把错误也合进文件;二是接受错误上屏、单独收集正常输出。在脚本中,如果我想在终端看到错误但又要正常输出进日志,就会用 command >>log 2> >(tee -a error.log >&2) 这类稍微复杂的重定向,但大多数场景用 >>log 2>&1 就够了。
另一种“输出还在屏幕”的情况是命令自身内部有缓冲机制,比如 python 的 print() 默认在非tty环境会开启块缓冲,输出不会及时写入文件。这时需要在命令层面处理,比如加 -u 参数或者调用 sys.stdout.flush() 。这种问题跟shell环境无关,需要用 stdbuf -ol 等工具干预,属于另一个话题,但排障思路是一样的:先判断是哪个通道的问题,再判断是shell层还是应用层的缓冲。
5.5 文件描述符耗尽与临时文件的清理
重定向使用频繁之后,还有一个隐藏问题:文件描述符被占用而不释放。特别是用 exec 3>file 自定义了fd之后,忘记关闭,后续脚本再打开新文件时就可能报 too many open files 。一般一个进程最多能同时打开1024个fd,在没有关闭的情况下频繁重定向,很容易逼近上限。
排查手段很简单:
ls -l /proc/$$/fd/
查看当前shell打开的fd列表。如果看到一堆没关闭的fd指向临时文件,就可以用 exec n>&- 显式关闭。养成“打开即关闭”的习惯,对长驻脚本非常重要。
另外, mktemp 创建的临时文件最好用trap清理,比如:
tmpfile=$(mktemp) trap 'rm -f "$tmpfile"' exit
这在写比较长的脚本时尤其重要,不然服务器 /tmp 下会堆积一堆你没意识到的垃圾文件。
5.6 实用技巧速查表
下面把重定向相关的典型写法和使用场景整理成一个表,方便日常翻阅:
| 写法 | 含义 | 典型场景 |
|---|---|---|
> file | stdout覆盖写入 | 重新生成日志、清空文件并输出 |
>> file | stdout追加写入 | 累积日志、增量记录 |
2> file | stderr覆盖写入 | 单独收集错误信息 |
2>> file | stderr追加写入 | 错误日志累积 |
> file 2>&1 | stdout和stderr都写文件 | 后台任务完整日志 |
2>&1 > file | 只有stdout写文件,stderr留终端 | 容易反的逻辑,慎用 |
&> file | bash简化写法,等价于 > file 2>&1 | 简短写法 |
< file | 文件内容作为stdin | 循环处理、wc统计 |
<< eof | here-document多行输入 | 生成配置文件、交互输入 |
<<< "str" | here-string单行输入 | 给命令快速喂参数 |
command | tee file | 输出同时给终端和文件 | 实时监控+归档 |
command >/dev/null 2>&1 | 彻底丢弃输出 | 只关心退出码 |
exec > file 2>&1 | 脚本内全局重定向 | 统一日志输出 |
exec 3> file | 自定义fd | 多路输出、交互式恢复 |
5.7 个人踩坑体会:从2>&1顺序到日志归档习惯
最后分享一个我自己的真实教训。以前写部署脚本时,为了省事把所有错误和标准输出都混在一个日志里, 2>&1 确实是加了,问题是没考虑到脚本会被重复执行。第二次跑的时候, > 直接清空了上一次的完整日志,结果排查问题时发现关键记录全没了,只能靠回忆。后来我改了习惯:所有脚本日志用 >> 追加,文件名带上日期,必要时再加个 logrotate 策略。这个改动看起来不起眼,却在后续排障时省了无数力气。
如果你在实践中遇到重定向相关的怪现象,我强烈建议先做两件事:第一,判断异常输出来自stdout还是stderr;第二,用 ls -l /proc/$$/fd/ 看看当前shell里各fd的真实去向。记住了,linux里几乎所有io皆文件,重定向不过是改变文件的指向。把这个核心逻辑刻在脑子里,再复杂的重定向组合也能顺利拆解。
到此这篇关于linux重定向完整指南:文件描述符、2>&1与日志管理实战的文章就介绍到这了,更多相关linux重定向内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论