tac 命令本质上是 cat 命令,但其目的是反向连接文件。 与 cat 类似,它具有方便的后备模式,如果没有提供输出文件,则打印到标准输出 (STDOUT),这使得它成为那些更常被用作懒人分页器(如 less 和 more)的命令之一,而不是其命名的功能。
cat 命令经常被过度使用和滥用,而 tac 命令通常被认为是像 ddate 或 cowsay 这样的玩笑命令。 它经常在愚人节的文章中被拿出来展示愚蠢的终端技巧。 因此,tac 实际上有其存在的合理理由,这可能会让人感到惊讶。
它实际上是一个有用的命令。
tac 的目的是什么?
tac 命令的手册页在描述其自身功能方面做得相当糟糕
Write each FILE to standard output, last line first.
如果按照字面意思理解这句话,tac 应该打印文件的最后一行,然后从第一行开始打印文件
$ cat metasyntactic.list
foobar
foo
bar
baz
$ tac metasyntactic.list
baz
foobar
foo
bar
然而,事实并非如此。 它的信息页更清楚地说明了这一点
copies each FILE (‘-’ means standard input),
or standard input if none are given,
to standard output, reversing the records
(lines by default) in each separately.
例如
$ tac metasyntactic.list
baz
bar
foo
foobar
忽略 tac 以相反顺序提供所有内容的事实,它有一些出人意料的有用且独特的选项。
Tac 和分隔符
正如信息页所指出的,文件不必按行分隔,这意味着 tac 对于 CSV 文件同样有效。 您可以使用 --separator 或 -s 选项以及文件中使用的分隔符来定义文件的分隔符字符。
对于 CSV 文件,字符可能是一个逗号 (,),但您可以定义任何字符。 但是,如果文件没有以分隔符字符结尾,那么您会得到意想不到的结果
$ tac --separator="," metasyntactic.csv
bazbar,foo,foobar
前两项之间没有分隔符字符。 文件的最后一条记录(最终分隔符后的字符串,在本例中为逗号)本身后面没有逗号,因此 tac 将其视为非记录。 为了解决这个问题,请使用 --before 或 -b 选项,该选项将分隔符字符放在每条记录之前
$ tac --separator="," --before metasyntactic.csv
baz,bar,foo,foobar
分隔符字符不必是单个字符。 它也可以是正则表达式 (regex)。
Tac 和正则表达式
对正则表达式的完整解释超出了本文的范围,但值得一提的是,扩展的 POSIX 通过 环境变量 得到支持。 扩展的正则表达式大大提高了正则表达式的可读性,为了简单起见,本示例使用了扩展的正则表达式。 假设您有一个文件,其中包含全部由整数分隔的字符串
$ cat metasyntactic.txt
foobar123foo456bar789baz898
您可以可靠地预测您关心的字符串由整数分隔,但您无法可靠地预测这些整数是什么。 这正是正则表达式旨在解决的问题。
要在您的 tac 命令中使用正则表达式,请在 --separator 定义之前使用 --regex 或 -r 选项。 此外,除非它已在您的环境中设置,否则您必须激活 REG_EXTENDED 环境变量。 您可以将此变量设置为除零以外的任何值来激活它,并且您可以通过所有常用方式来做到这一点
- 为您正在使用的 shell 会话导出变量。
- 在您的 shell 配置文件(例如 ~/.bashrc)中设置环境变量。
- 将环境变量添加到 tac 命令的前面(在 Bash、Zsh 和类似 shell 中),如下例所示
$ REG_EXTENDED=1 tac --regex \
--separator='[0-9]+' metasyntactic.txt
89baz898bar765foo432foobar1
即使使用 --before 选项,正则表达式选项也无法很好地处理未终止的记录。 如果此功能对您很重要,您可能需要调整源文件。
何时使用 tac
这些简单但有用的解析选项使得 tac 值得用作一个简单、极简的解析命令。 对于那些不值得为此编写 AWK 或 Perl 表达式的简单任务,tac 可能是一个明智的解决方案。
显然,tac 命令是有限的,因为它除了反转记录之外,不会以任何方式操作记录。 但有时这正是您需要的唯一列表操作。
例如,如果您正在打包软件以进行分发,那么拥有一个安装所需的依赖项列表是很常见的。 根据您收集此列表的方式,您可能会按照您确定依赖项所需的顺序而不是它们必须安装的顺序来排列它。
这种做法相对常见,因为编译器错误首先会影响高级依赖项。 也就是说,如果您的系统缺少 libavcodec,那么 GCC 会停止并提醒您; 但是由于 GCC 没有机会探测您的系统是否存在 libvorbis 和 libvpx,例如,它无法告诉您这些依赖项也缺失(并且通常需要在编译 libavcodec 之前存在于您的系统上)。
因此,当您发现您的系统需要哪些库来构建库所需的库(等等)时,您的依赖项列表会以自上而下的形式增长。 在这样一个过程结束时,tac 是反转该列表的快速简便方法。
另一个常见的烦恼是日志文件。 条目通常附加到日志文件中,因此管理员使用 tail 来查看最新的错误。 这效果很好,但有时您想查看“一块”条目,而不知道需要回溯多远。 通过管道传输到 less 或 more 的 tac 命令将最新的条目放在屏幕顶部。
最后,许多配置文件对于给定部分没有明确的终止标记。 您可以查找 awk 和 sed 命令来设计一种方法来确定配置文件中的块何时结束,或者您可以使用 tac 来反转顺序,这样一旦您的解析器在该块中找到第一个相关条目,它也知道何时停止读取,因为以前的标题现在变成了页脚。
继续使用 Tac
tac 还有很多其他很棒的用途,并且可能有许多原因表明 tac 太过简陋而无法成为解决方案。 但是,您的系统可能已经安装了它,因此请记住这个命令,下次当您在工作流程中发现确实真的需要反向攻克的边缘情况时。
1 条评论