假设两个文件具有以下内容:
$ cat ttest1.txt
x = 1
x = 3
y = 5
$ cat ttest2.txt
x = 4
x = 10
y = 3
我想递归地 grep 文件x
并打印每个文件中的最后一个实例。所以,期望的输出是
ttest1.txt:x = 3
ttest2.txt:x = 10
以下grep
和的组合tail
适用于一个文件,但不适用于多个文件。
$ grep x ttest1.txt
x = 1
x = 3
$ grep x ttest1.txt | tail -n 1
x = 3
$ grep -r x ttest* | tail -n 1
ttest2.txt:x = 10
我该如何解决这个问题?
答案1
当你这样做时
grep -r x ttest*
你得到一个流
ttest1.txt:x = 1
ttest1.txt:x = 3
ttest2.txt:x = 4
ttest2.txt:x = 10
如果你tail -1
这样做,你就会得到ttest2.txt:x = 10
.这正如预期的那样。
但是您希望grep | tail
在每个单独的文件上完成组合,而不是在合并的流上完成。所以:对每个单独的文件执行此操作。
for f in ttest* ; do
grep x "$f" | tail -n 1
done
答案2
使用awk
,您可以使用正则表达式字符串(请参阅动态正则表达式),记住每个输入文件的最后一个匹配并打印它:
$ awk -v regex="x" 'FNR==1 && m!=""{print m;m=""}
$0 ~ regex{m=FILENAME ":" $0}
END{if (m!="")print m}' ttest*
ttest1.txt:x = 3
ttest2.txt:x = 10
递归地与 结合find
:
$ find . -name 'ttest*' -type f -exec awk -v regex="x" '
FNR==1 && m!=""{print m;m=""}
$0 ~ regex{m=FILENAME ":" $0}
END{if (m!="")print m}' {} +
./ttest1.txt:x = 3
./ttest2.txt:x = 10
答案3
使用 GNU sed:
sed -sn '/x/h; $ { x; F; p; }' ttest*
输出:
ttest1.txt
x = 3
ttest2.txt
x = 10
paste -d: - -
如果您希望结果在一行中,请通过此选项。如果您不想从不包含-change 命令x
后包含检查的文件中输出x
,例如:{ x; /./ { F; p; z; }; }
答案4
这是一种方法:
grep x <yourpath>* | tac | awk 'BEGIN{FS=":"};seen!=$1{print $0;seen=$1}'
tac 反转 grep 结果以准备 awk 操作,然后仅打印每个文件中的第一个匹配项