我有一个文件,其中不同的元素在多行上重复。 我的文件包含这样的行:
1 $element_(1)
10 $element_(2)
20 $element_(1)
30 $element_(3)
40 $element_(1)
50 $element_(2)
60 $element_(3)
70 $element_(1)
我想获取每个元素的最后一次出现并将它们放入文件中
resultfile
。
50 $element_(2)
60 $element_(3)
70 $element_(1)
我试过了
for i in {1..8000} do
grep $element_\($i\) sourcefile | tail -1 >> resultfile
done
但它给了我错误。此外,如何区分作为字符串名称一部分的
$
和用于增加我正在搜索的元素数量的$
?
另外,我也不知道文件中将包含多少个元素,因此我将 8000 作为最大值,但它可以更少或更多。
您可以告诉 grep 在找到第一个匹配项 (
-m 1
) 后停止,并且要使该匹配项成为文件中的最后一个匹配项,您可以将文件反向传输到 grep:
for i in {1..8000}; do
tac sourcefile | grep -m 1 "\$element_($i)"
done > resultfile
我还将输出重定向移到循环之外,并修复了模式中的引用:我引用整个模式;第一个
$
必须被转义,这样 shell 就不会尝试扩展变量 $element_
,并且括号不能被转义,否则 grep 会认为它是一个捕获组。在您的尝试中,您正确地转义了它们,但这里通过引用整个模式来避免这种情况。
单引号模式通常更容易,因此我们不必关心 shell 扩展,但在这种情况下,我们希望
$i
真正扩展。
您的尝试存在语法错误,因为大括号后缺少
;
。
如果行的顺序必须与输入文件中的顺序相同,我们可以在前面添加行号 (
nl
) 并在最后按行号排序 (sort -n
),然后再次使用 cut
删除它们:
for i in {1..8000}; do
nl sourcefile | tac | grep -m 1 "\$element_($i)"
done | sort -n | cut -f 2 > resultfile
如果我们知道元素索引是连续的,并且一旦找不到元素就可以停止,我们可以按如下方式调整循环(仍然假设我们希望按照输入文件中出现的顺序保留元素):
i=0
while true; do
((++i))
nl sourcefile | tac | grep -m 1 "\$element_($i)" || break
done | sort -n | cut -f 2 > resultfile
这使用递增计数器而不是预定序列。如果管道的退出状态不为零,即 grep 找不到该元素,我们将退出循环。