我想实现类似这样的目标
grep -v '^$' <myfile-with-blank-lines-at-the-end> | tail -n 1
基本上,我有一个文件,里面有一个唯一的编号,后面跟着一个逗号,我想抓取最后一个 id,即使下面可能有空行。目标是只使用 tail
答案1
目标是仅使用
tail
。
不可能。
分析
您使用的两个工具都是用Unix 哲学心里:
- 编写只做一件事的程序并把它做好。
- 编写程序来协同工作。
- 编写程序来处理文本流,因为这是一个通用接口。
在您的示例中,它们协同工作,将文本流传递给另一个。每个都做“一件事”:
grep
的事情是“在文件中搜索某种模式”,tail
的事情是“复制文件的最后部分”。
这些事物是正交的;没有共同的组成部分。
tail
不关心内容,特别是不关心是否有任何行是空的。实际上,您无法使用 sole 做您想做的事情tail
。如果有人扩展tail
来处理您的情况,我会说这是错误的事情。
grep
不关心是否已到达最后一行。锚点\Z
设计用于匹配“字符串末尾以及字符串中最后一个换行符之前(如果有)”或甚至“字符串末尾以及字符串中所有尾随换行符之前(如果有)”(取决于风格,请参阅这)。后者对您的情况可能有用,但grep
似乎根本不支持\Z
(或任何类似的东西)。
更多通用工具
有些工具的“一件事”足以涵盖这两项任务。您当然可以使用通用文本处理工具,例如sed
或awk
。
确实,以下每个命令几乎都执行原始命令所执行的操作:
sed -n '/./ h; $ {g;p}'
awk '{if ($0 != "") buffer=$0} END {print buffer}'
几乎,因为如果没有非空行(也包括完全为空的输入的情况),输出中将有一个空行。在这种情况下,您的原始命令不会产生任何结果。我们需要一些逻辑来真正模仿您的命令。像这样:
sed -n '/./ h; $ {g;s/^$//;t;g;p}'
awk '{if ($0 != "") buffer=$0} END {if (buffer != "") print buffer}'
即使没有这种额外的逻辑,我也发现这两种解决方案都不如您的简洁grep … | tail …
。这并不奇怪。对于一般工具来说,非常简单的脚本(或/和默认选项)可能不会做任何有趣的事情;而一个好的专用工具则被设计成用相当简单的语法来完成其最常见的任务。
这意味着你不应该期望找到一个通用工具,让你像 一样轻松地完成你想做的事情grep … | tail …
。如果管道很长而且很复杂,sed
那么awk
可能是最好的方法,一种简化的方法。但您的用例是“搜索(不)匹配模式的行并选择最后一行”。这正是 和 的grep
用途tail
。
吻
保持简单明了。您的原始命令非常简单。只需要对这两个工具有一些基本了解,就可以大致了解发生了什么。我明白了grep … | tail …
,我马上就能说我们正在寻找某种模式,并会选择最后几行。因为这就是这两个工具分别做的事情。
我明白了awk …
,或者sed …
,它可以是任何东西。即使某些(深奥的?高尔夫的?)语言允许我们使用更简洁的代码来完成你的特定任务,我仍然会选择grep … | tail …
,特别是如果代码要持久并得到维护的话。
改进
我可以稍微简化一下你的原始命令,但是它的形式grep … | tail …
是:
# original, for comparison, commented out
# grep -v '^$' | tail -n 1
# simplified
grep . | tail -n 1