tr只能字符级单对单映射,不支持子串替换;适用场景为删控符、大小写转换、去重空白等流式清洗,禁用于utf-8宽字符及二进制数据。

tr 命令根本不能“替换字符串”,只能一对一映射字符
很多人用 tr 想把 "abc" 替成 "xyz",结果发现不管用——因为 tr 不是 sed 或 awk,它只做字符级的单对单映射,不是子串替换。tr 'a' 'x' 可以,tr 'ab' 'xy' 表面看行,但实际是把 a→x、b→y 分开映射;一旦源字符集比目标长(比如 tr 'abc' 'xy'),就会报错或截断,不同系统行为还不一致。
常见错误现象:tr 'old' 'new' 本意是替换整个单词,结果所有 <code>o、l、d 全被各自替成 n、e、w,文件彻底乱掉。
- 使用场景:清理日志里的控制字符(如
tr '\015\012' '\n\n')、统一大小写(tr 'a-z' 'A-Z')、删空格(tr -d ' \t') - 参数差异:
-d删除字符,-s压缩重复字符(如tr -s '\n'把多个换行压成一个),-c对补集操作(tr -cd '[:alnum:]'只留字母数字) - 注意
tr不支持正则、不支持多字节 UTF-8 字符(比如中文基本不可靠),遇到宽字符先用iconv或换工具
管道里用 tr 处理命令输出时,别漏掉 stderr
tr 默认只读取 stdin,而很多命令的错误信息走 stderr,直接 cmd | tr 'a' 'A' 会丢掉报错内容,看着像没反应,其实是错误被绕过了。
- 想一并处理:用
cmd 2>&1 | tr 'a' 'A'把 stderr 合并进 stdout 再传给tr - 只想处理 stdout、保留 stderr 原样输出:不用动,这是默认行为,但得确认你真不需要改错信息
- 性能影响极小——
tr是纯流式处理,内存占用恒定,适合大文件,但别指望它加速复杂逻辑
Linux 下 tr 和 macOS 的行为差异在哪
macOS(BSD)版 tr 对字符范围更严格。比如 tr 'a-z' 'A-Z' 在 Linux(GNU)下通常能用,但在 macOS 上可能报 Illegal byte sequence,尤其当终端 locale 是 UTF-8 且输入含非 ASCII 字符时。
- 兼容写法:显式用 POSIX 字符类,
tr '[:lower:]' '[:upper:]',两边都生效 - 避免硬编码范围,比如别写
tr '\141-\172' '\101-\132'(八进制),可读性差还容易跨平台出错 - 检查当前环境:
locale看LC_CTYPE,设成C(LC_CTYPE=C tr 'a-z' 'A-Z')可绕过多数 UTF-8 问题,但会丢失本地化排序规则
该用 tr 还是该换 sed?看这三点判断
别为了省一个命令硬套 tr。三秒内能决定:要改的是单个字符还是字符串?要不要上下文匹配?输进来的是纯文本还是带结构的数据?
- 用
tr:删控制符、大小写转换、去重空白、字符清洗(如日志预处理) - 换
sed:替换固定子串(sed 's/foo/bar/g')、按行条件处理(/^ERROR/ s/$/ [handled]/)、需要捕获组或引用 - 再复杂就上
awk:字段分割、计数、条件聚合——tr做不了的事,硬塞只会让脚本难调试、难维护
最常被忽略的一点:tr 的输入必须是文本流,遇到二进制数据(比如 PDF、图片管道过来)会崩,连提示都可能没有,只静默损坏。真不确定输入类型,先用 file 或 head -c 100 | od -c 看一眼。










