说实话,rev这个命令在实际使用中确实非常高效,但隐藏的陷阱也不少。它能安全、快速地反转文件每一行的内容,不过它只执行字节级别的翻转,并不关心字符编码的逻辑——这意味着中文文本虽然看起来显示出来了,但大概率会出现错位。如果文件是 UTF-8 编码,基本上还能保持可读性;但如果是 GBK 或混合编码,那输出几乎就是一片乱码。这些前提如果不提前了解清楚,翻车几乎是必然的。
那么,rev反转每行的底层行为到底是什么?
核心问题在于:rev本质上就是一个逐行读取的工具。它把每一行视为一串字节,从最后一个字节开始,逐个往前输出。换行符 \n 不会被触碰,只作为行的边界保留。因此,行数、行顺序、换行位置全都保持不变,唯一变化的就是行内字符的顺序——完全颠倒。
- 对纯 ASCII 字符(比如日志 ID、域名、十六进制字符串)来说,完全没问题,非常可靠。
- 对 UTF-8 编码的中文,只要你能确认文件确实是 UTF-8(用
file -i filename检查一下),rev通常也能“正确”翻转。原因是 UTF-8 是自同步的,字节序列倒过来后,utf8proc这类库或终端可能还能渲染出可读的结果,但语义已经被破坏了。比如“你好”变成了“好你”,每个汉字的 UTF-8 字节块没被拆开,但顺序反了,意思也就变了。 - 如果文件是 GBK、Big5 或 Latin-1 这些编码,
rev会把多字节字符的字节强行倒序,导致解码彻底失败。显示出来就是一堆乱码或方块。
那么,怎么安全地用 rev 去反转一个文本文件?
最常用、也最推荐的用法,就是直接传文件名,不加任何参数:
rev data.txt
这个命令等价于 cat data.txt | rev,但少了一次进程调度,更轻量。
- 想原地修改文件?
rev不支持-i参数,得用临时文件:rev data.txt > tmp && mv tmp data.txt - 想跳过空行再反转?用
grep -v '^$'过滤后再管道接rev:grep -v '^$' data.txt | rev - 只想反转包含特定关键词的行?
grep 'error' access.log | rev - 反转后还想截取字段?配合
cut或awk就能实现:echo "/var/log/syslog" | rev | cut -d'/' -f1 | rev(这可以用来提取一段路径的最后一部分,也就是 basename)。
rev处理中文时容易踩的坑,必须得单独拿出来说。
举个现象:文件里有“测试abc”,用 rev 后变成了“cba试测”。看起来像“cba”在前、“试测”在后——但实际上,是 UTF-8 字节倒序后,终端按照原来的编码重新解析时,把“测”这个字的 UTF-8 三字节块当成了三个独立的拉丁字符来渲染。结果就是一片混乱。
- 千万别指望
locale设置能“修复”rev对中文的处理——rev本身就没有编码感知能力。 - 想真正按 Unicode 字符(而不是字节)来反转,就得换工具了:
perl -C -nle 'print scalar reverse'或python3 -c "import sys; [print(line.rstrip()[::-1]) for line in sys.stdin]"。 - 如果只是调试用(比如看 base64 片段倒序后是否匹配),用
rev没问题。但如果是用来做数据清洗或生成正式输出,尤其是中文场景,务必先确认编码,再决定是否用rev。
当然,rev也不是唯一的选择。
当 rev 不可用时(这种情况比较少见,但某些精简的容器镜像可能没装 util-linux),或者需要更可控的反转逻辑时:
- macOS 和 Linux 都可以用
sed来模拟(性能差,只适合简单的 ASCII):sed '/^$/!s/./&n/g;s/n$//;s/(.)(.*)/21/' file.txt(说实话,不推荐这么干)。 - 用
tac+rev组合可以实现真正的完整反转——先倒行序,再倒每行:tac file.txt | rev。 - 二进制文件(如图片、PDF)绝对不要用
rev,会破坏结构。当file命令报出data类型时,就该停下了。
说到底,真正要小心的不是“会不会用”,而是“输入到底是什么编码”——这个判断一旦出错,rev 的输出就不可逆了,而且往往很难察觉。
