tr 命令详解与参数实测(Ubuntu 22.04)
tr 命令逐参数实测:大小写区间与字符类、-d 删除、-s 压缩、-c 补集及 -cs 组合,只读 stdin 与无正则两大限制,全部回显在 Ubuntu 22.04(GNU coreutils 8.32)实测通过。
tr 命令详解与参数实测(Ubuntu 22.04)
目标:全面掌握 GNU tr 的字符转换能力——大小写区间与字符类两种写法、
-d删除、-s压缩重复、-c补集及-cs组合,以及「只读标准输入、不支持文件参数与正则」两大限制,全部命令与回显已在 Ubuntu 22.04(GNU coreutils 8.32)实测通过。
一、环境信息
本节记录实测环境与测试数据,便于读者按相同条件复现本文全部命令与回显。
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- tr 版本:GNU coreutils 8.32(
tr --version实测) - 默认 locale:
LANG未设置(等效 C locale) - 实测工作目录:
/root/demo/tr/ - 测试数据:
words.txt(2 行英文样例,用于文件参数报错演示);其余演示通过echo/printf管道提供输入
二、命令概览
2.1 语法与测试数据准备
tr 的基本语法为 tr [OPTION]... SET1 [SET2]:从标准输入读取数据,把 SET1 中的字符按位置映射为 SET2 中的字符(转换),或按选项删除、压缩字符,结果写到标准输出。它不接受文件参数,只吃管道或重定向。准备一个英文样例文件供后文报错演示使用:
mkdir -p /root/demo/tr
cat > /root/demo/tr/words.txt <<'EOF'
Hello World
Linux Text Processing
EOF
验证数据就绪:
$ ls -l /root/demo/tr
total 4
-rw-r--r-- 1 root root 34 Oct 15 07:40 words.txt
$ cat /root/demo/tr/words.txt
Hello World
Linux Text Processing
💡 说明:tr 的绝大多数用法输入都是短文本,本文统一用
echo/printf管道实测,输出即回显;words.txt 只在 3.6 节演示「传文件参数会报错」。
2.2 --help 全量回显
tr --help 输出用法与全部可解释序列(实测原样保留,可作为字符集写法速查):
Usage: tr [OPTION]... SET1 [SET2]
Translate, squeeze, and/or delete characters from standard input,
writing to standard output.
-c, -C, --complement use the complement of SET1
-d, --delete delete characters in SET1, do not translate
-s, --squeeze-repeats replace each sequence of a repeated character
that is listed in the last specified SET,
with a single occurrence of that character
-t, --truncate-set1 first truncate SET1 to length of SET2
--help display this help and exit
--version output version information and exit
SETs are specified as strings of characters. Most represent themselves.
Interpreted sequences are:
\NNN character with octal value NNN (1 to 3 octal digits)
\\ backslash
\a audible BEL
\b backspace
\f form feed
\n new line
\r return
\t horizontal tab
\v vertical tab
CHAR1-CHAR2 all characters from CHAR1 to CHAR2 in ascending order
[CHAR*] in SET2, copies of CHAR until length of SET1
[CHAR*REPEAT] REPEAT copies of CHAR, REPEAT octal if starting with 0
[:alnum:] all letters and digits
[:alpha:] all letters
[:blank:] all horizontal whitespace
[:cntrl:] all control characters
[:digit:] all digits
[:graph:] all printable characters, not including space
[:lower:] all lower case letters
[:print:] all printable characters, including space
[:punct:] all punctuation characters
[:space:] all horizontal or vertical whitespace
[:upper:] all upper case letters
[:xdigit:] all hexadecimal digits
[=CHAR=] all characters which are equivalent to CHAR
Translation occurs if -d is not given and both SET1 and SET2 appear.
-t may be used only when translating. SET2 is extended to length of
SET1 by repeating its last character as necessary. Excess characters
of SET2 are ignored. Only [:lower:] and [:upper:] are guaranteed to
expand in ascending order; used in SET2 while translating, they may
only be used in pairs to specify case conversion. -s uses the last
specified SET, and occurs after translation or deletion.
GNU coreutils online help: <https://www.gnu.org/software/coreutils/>
Report any translation bugs to <https://translationproject.org/team/>
Full documentation <https://www.gnu.org/software/coreutils/tr>
or available locally via: info '(coreutils) tr invocation'
💡 说明:帮助明确了三条行为约定,都是后文实测的依据——其一,SET2 比 SET1 短时用最后一个字符补齐,SET2 多余字符被忽略;其二,
-s作用于「最后指定的 SET」且发生在转换/删除之后;其三,[:lower:]与[:upper:]只有成对用于转换时才有顺序保证。
2.3 版本确认
$ tr --version | head -1
tr (GNU coreutils) 8.32
三、参数逐项实测
本章逐项实测每个参数与典型写法:每节先给命令与真实回显,再解释行为要点。
3.1 大小写转换:区间写法与字符类写法
最基本的转换:SET1 写小写字母区间 a-z,SET2 写大写字母区间 A-Z,按位置一一映射:
$ echo 'hello world' | tr 'a-z' 'A-Z'
HELLO WORLD
更推荐用字符类(POSIX class)写法,语义清晰且不依赖区间假设。大写转小写:
$ echo 'HELLO World 123' | tr '[:upper:]' '[:lower:]'
hello world 123
小写转大写:
$ echo 'Linux Text Processing' | tr '[:lower:]' '[:upper:]'
LINUX TEXT PROCESSING
💡 提示:
[:upper:]必须整体放在单引号里,漏掉方括号或引号会被 shell 展开出意想不到的文件名。字符类只有在转换(同时给 SET1 与 SET2)且成对使用[:lower:]/[:upper:]时行为才有保证,见 2.2 节帮助原文。
3.2 字符集区间与末位补齐
SET1 可以拼多个区间;SET2 比 SET1 短时按帮助约定用最后一个字符补齐:
$ echo 'abc 123' | tr 'a-z0-9' 'A-Z#'
ABC ###
SET1 是 26 个小写字母加 10 个数字共 36 个字符;SET2 是 26 个大写字母加 # 共 27 个字符,不足的 10 个位置全部用最后一个字符 # 补齐——所以所有数字都变成了 #。这条规则让「剩余字符统一映射为某字符」零成本实现。
区间+映射的另一个经典应用是 ROT13 混淆(字母表平移 13 位,再执行一次即还原):
$ echo 'hello' | tr 'a-zA-Z' 'n-za-mN-ZA-M'
uryyb
3.3 -d:删除字符
-d 删除 SET1 中的所有字符,此时不需要 SET2。删除数字:
$ echo 'abc123def456' | tr -d '0-9'
abcdef
删除指定符号:
$ echo 'a-b-c' | tr -d '-'
abc
删除换行符(把多行拼成一行)。因为输出不再有换行,紧跟一条 od 按字符回显字节,证明结果末尾连换行符都没有:
$ printf 'line1\nline2\nline3\n' | tr -d '\n'
line1line2line3
$ printf 'line1\nline2\nline3\n' | tr -d '\n' | od -An -c
l i n e 1 l i n e 2 l i n e 3
删除制表符:
$ printf 'a\tb\tc\n' | tr -d '\t'
abc
💡 提示:
tr -d '\n'是「合并多行为一行」的最快写法,但注意输出末尾没有换行符,直接拼接下一命令时记得补\n或用echo收尾。
3.4 -s:压缩连续重复字符
-s 把 SET 中字符的连续重复压成单个。压缩空格——清理多余空白的常用招:
$ echo 'hello world' | tr -s ' '
hello world
压缩指定字符集合:
$ echo 'aaabbbccc' | tr -s 'abc'
abc
$ echo 'woooow!!!' | tr -s 'o!'
wow!
第二条里 o 与 ! 都在 SET 中,各自的连续重复分别被压缩,互不影响。
💡 提示:
-s作用于「最后指定的 SET」且发生在转换或删除之后(2.2 节帮助原文)——所以tr -s 'x' 'y'是先转换再压缩,理解这一点才能预测组合行为。
3.5 -c:取补集,及 -cs 组合
-c 对 SET1 取补集(除指定字符之外的所有字符)。删除「非数字」:
$ echo 'abc123abc' | tr -cd '0-9'
123
-cd 组合的语义是:把「0-9 的补集」(字母、空格、换行等)全部删掉,只留数字。注意输出 123 后连换行符也被删了(换行符属于补集)。
-cs 组合是提取单词清单的经典:把「字母数字的补集」压成换行,效果即「按非字母数字分词、每词一行」:
$ echo 'Hello, World! 123' | tr -cs 'a-zA-Z0-9' '\n'
Hello
World
123
💡 提示:
-cs 'a-zA-Z0-9' '\n'值得背下来——从任意文本中拉出单词/编号清单,比写 sed/awk 正则更直接。
3.6 只读标准输入:传文件参数会报错
tr 不接受文件参数,把文件名跟在 SET 后面会被当作多余操作数拒绝:
$ tr a-z A-Z /root/demo/tr/words.txt; echo exit=$?
tr: extra operand '/root/demo/tr/words.txt'
Try 'tr --help' for more information.
exit=1
正确姿势是用重定向或管道喂 stdin:
$ echo 'Linux Text Processing' | tr '[:lower:]' '[:upper:]'
LINUX TEXT PROCESSING
💡 提示:想直接处理文件用
tr SET1 SET2 < 文件;这一点与 cut、sort(都接受文件参数)不同,也与 rev(接受文件参数)不同,是 tr 最常被记错的地方。
3.7 不支持正则:字符一律按字面量处理
tr 的 SET 是字符集合,不是正则表达式。. 就是句点本身:
$ echo 'a.c 1+2' | tr '.' '_'
a_c 1+2
[a-z] 里的方括号也只是两个普通字符(真正起作用的是区间 a-z):
$ echo 'a[b]c' | tr '[a-z]' '[A-Z]'
A[B]C
第二条输出中 [、] 原样保留,证明方括号没有正则含义——小写转大写是区间 a-z 到 A-Z 的映射在起作用。需要正则能力的字符处理请用 sed 或 awk。
💡 技术拓展:字符/文本转换不止 tr 一家,常见工具横向对比——
工具 能力层次 适用场景 tr 字符级映射、删除、压缩,无正则 单字符维度批量转换(大小写、去数字、压空格,本文主角) sed 行级替换,支持正则与 \U/\L大小写转换需要上下文匹配的替换、多字节中文 awk 字段级处理, toupper()/tolower()按列转换、边转换边统计 iconv 整文件编码转换 GBK ↔ UTF-8 等编码迁移 本文以 tr 为主线实测其字符级能力;涉及正则与中文的转换见五、Q4,sed 用法见《sed 命令详解与参数实测(Ubuntu 22.04)》。
四、实战示例
前面各节覆盖了单个参数与组合行为,本节给三个可直接套用的场景。
1. 文本清洗成单词清单——标点与空格统一压成换行:
$ echo 'Hello, World! 123' | tr -cs 'a-zA-Z0-9' '\n'
Hello
World
123
2. 字段脱敏——删除字符串中的数字,只留可读部分:
$ echo 'abc123def456' | tr -d '0-9'
abcdef
3. ROT13 编解码——同一条命令再执行一次即可还原,适合轻量混淆:
$ echo 'hello' | tr 'a-zA-Z' 'n-za-mN-ZA-M'
uryyb
五、注意事项与常见问题
本节汇总 tr 使用中最容易踩坑的四个问题,均对应前文实测结论。
Q1:tr 能直接传文件名处理文件吗?
不能。tr 只读标准输入,传文件参数会报 extra operand 错误并退出码 1(3.6 节实测)。正确写法是 tr SET1 SET2 < 文件 或 cat 文件 | tr SET1 SET2。
Q2:tr 里的 .、[]、* 是正则吗?
不是。tr 完全没有正则,SET 是字符集合:. 就是句点、[ ] 就是方括号本身(3.7 节实测),[CHAR*REPEAT] 是 tr 自己的重复语法而非正则量词。需要正则的转换、删除、替换交给 sed。
Q3:SET1 与 SET2 长度不一致时会怎样?
两条规则(2.2 节帮助原文 + 3.2 节实测):SET2 短于 SET1 时,用 SET2 的最后一个字符循环补齐(数字全变 # 的实测);SET2 长于 SET1 时,多余字符被忽略。利用补齐规则可以一行实现「部分映射 + 其余统一映射」。
Q4:tr 能处理中文等多字节字符吗?
tr 的字符集按单字节语义设计,区间与补集都是字节级操作,对 UTF-8 中文这类多字节文本容易破坏编码,不建议使用。中文大小写转换可用 sed 的 \U/\L、awk 的 toupper()/tolower()(UTF-8 locale 下),编码转换交给 iconv。
六、参考链接
- GNU Coreutils 官方手册 tr 章节:https://www.gnu.org/software/coreutils/manual/html_node/tr-invocation.html
- Ubuntu 22.04 (jammy) 在线 man 手册 tr(1):https://manpages.ubuntu.com/manpages/jammy/en/man1/tr.1.html
- POSIX 字符类规范:https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html
- 本系列关联篇:《sed 命令详解与参数实测(Ubuntu 22.04)》《rev 命令详解与参数实测(Ubuntu 22.04)》