覆盖写入,>> 追加写入;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 都会被改写,不只是当前命令。
Shell本身是一个用C语言编写的程序,它是用户使用Linux的桥梁。Shell既是一种命令语言,又是一种程序设计语言。作为命令语言,它交互式地解释和执行用户输入的命令;作为程序设计语言,它定义了各种变量和参数,并提供了许多在高级语言中才具有的控制结构,包括循环和分支。它虽然不是Linux系统核心的一部分,但它调用了系统核心的大部分功能来执行程序、建立文件并以并行的方式协调各个程序的运行。因此,对于用户来说,shell是最重要的实用程序,深入了解和熟练掌握shell的特性极其使用方法,是用好Linux系统
-
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 映射。









