跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

Linux shell 输入输出重定向高级用法

覆盖写入,>> 追加写入;2>&1 复制描述符而非合并流;exec 重定向持久生效;管道默认不传 stderr。 重定向符号搞混了:> 和 >> 的实际行为差异
>
是覆盖写入,
>>
是追加写入——但很多人没意识到,它们在打开文件时的底层动作完全不同。比如
>
会先清空目标文件(哪怕写入失败),而
>>
总是在末尾 seek,不破坏原有内容。 如果脚本里反复用
>
写同一个日志文件,旧日志必然丢失,不是“没写进去”,是被主动截断了
>>
在 NFS 或某些只读挂载下可能报
Permission denied
,因为追加需要写权限+执行权限(用于判断目录是否可写) 某些 shell(如 dash)对
>>
的原子性支持较弱,高并发写同一文件时可能出现行交错,别指望它替代日志轮转工具
echo "start" > log.txt echo "done" >> log.txt # 这行才真正在末尾加内容
2>&1 不等于 “把所有输出都扔进一个地方”
2>&1
只是把 stderr 的文件描述符复制成和 stdout 一样, 不是合并流内容 。如果 stdout 被重定向到文件,stderr 就跟着去那个文件;但如果 stdout 是终端,stderr 也还是终端——前提是没被提前改过。 错误写法:
cmd 2>&1 >out.log
—— 这里
2>&1
发生在
>out.log
之前,所以 stderr 指向的是原始 stdout(通常是终端),结果只有 stdout 进文件,stderr 还在屏幕上 正确顺序:
cmd >out.log 2>&1
,或更清晰的
cmd &>out.log
(bash/zsh 支持,dash 不支持) 如果要分别捕获又想按时间顺序看,
script -qec 'cmd' /dev/null |& grep --line-buffered "error"
比单纯重定向更可控 exec 重定向会持续影响后续命令
exec
对文件描述符的操作是进程级的,一旦执行,整个 shell 环境的 stdin/stdout/stderr 都会被改写,不只是当前命令。 CentOS Linux 7.9.2009 CentOS Linux 7.9.2009是传统CentOS Linux 7的最后主要版本,也是很多企业历史服务器中仍可能遇到的系统版本。它以稳定、兼容RHEL 7生态、文档丰富和软件支持广泛著称,曾长期用于Web服务、数据库、虚拟化节点和企业内部业务系统。不过CentOS Linux 7已于2024年6月30日停止维护,现在继续使用会面临安全补丁缺失风险。该版本更适合旧业务迁移、历史环境恢复或离线兼容性测试。 下载
exec 3>tmpfile
开了个新 fd 3,安全;但
exec >log.txt
之后,所有没显式指定输出的命令(包括
echo
、
ls
、甚至 for 循环里的变量打印)都会进
log.txt
恢复原始 stdout?不能靠
exec > /dev/tty
硬切,因为终端可能不是
/dev/tty
;稳妥做法是提前备份:
exec 3>&1; ...; exec 1>&3 3>&-
在脚本里滥用
exec >...
容易导致调试时“什么都没输出”,还以为命令没跑,其实是全写进文件了却忘了查 管道 + 重定向组合时,stderr 去哪了 管道默认只传 stdout,stderr 默认仍走终端。但很多人以为
cmd | grep "foo"
会过滤掉所有输出,结果错误信息还是刷屏。
cmd 2>&1 | grep "foo"
才能把 stderr 也送进管道;但注意,grep 只处理行,而 stderr 可能是无换行的进度条或二进制乱码 如果只想静默错误:
cmd 2>/dev/null
,别写成
cmd 2> /dev/null
(空格会让 shell 把
/dev/null
当作命令参数) 某些命令(如
make
)会检测 stdout 是否为终端,自动切换输出格式;重定向后可能丢失颜色或进度条,这不是 bug,是设计行为 事情说清了就结束。重定向看着简单,但 fd 层面的细节一错,输出就不可控——尤其在脚本里嵌套多次重定向时,建议用
ls -l /proc/$$/fd
实时看当前 shell 的 fd 映射。

相关文章