我正试图找到一个简洁的贝壳单线,它会给我所有的 文件中的行直到某种模式。
用例是将所有行转储到日志文件中,直到找到一些为止 表示服务器已重新启动的标记。
以下是一种愚蠢的shell方式:
tail_file_to_pattern() {
pattern=$1
file=$2
tail -n$((1 + $(wc -l $file | cut -d' ' -f1) - $(grep -E -n "$pattern" $file | tail -n 1 | cut -d ':' -f1))) $file
}
在stdin上获取文件的稍微更可靠的Perl方式:
perl -we '
push @lines => $_ while <STDIN>;
my $pattern = $ARGV[0];
END {
my $last_match = 0;
for (my $i = @lines; $i--;) {
$last_match = $i and last if $lines[$i] =~ /$pattern/;
}
print @lines[$last_match..$#lines];
}
'
当然,您可以更有效地打开文件, 寻求最终并寻找回来,直到找到匹配的行。
在 first 出现时很容易打印所有内容,例如:
sed -n '/PATTERN/,$p'
但我没有想出一种方法来打印 last 的所有内容 次数。
答案 0 :(得分:6)
这是一个仅限sed的解决方案。要打印$file
中的每一行,以与$pattern
匹配的最后一行开头:
sed -e "H;/${pattern}/h" -e '$g;$!d' $file
请注意,与您的示例一样,只有在文件包含模式时才能正常工作。否则,它输出整个文件。
以下是括号中的sed命令的详细信息:
另请注意,对于非常大的文件,它可能会变慢,因为任何单遍解决方案都需要在内存中保留一堆行。
答案 1 :(得分:4)
或者:tac "$file" | sed -n '/PATTERN/,$p' | tac
编辑:如果您没有tac
通过定义
tac() {
cat -n | sort -nr | cut -f2
}
丑陋而POSIX。
答案 2 :(得分:4)
逐行将数据加载到数组中,并在找到模式匹配时抛出数组。打印出最后剩下的东西。
while (<>) {
@x=() if /$pattern/;
push @x, $_;
}
print @x;
作为一个单行:
perl -ne '@x=() if /$pattern/;push @x,$_;END{print @x}' input-file
答案 3 :(得分:3)
我建议简化shell脚本:
tail -n +$(grep -En "$pattern" "$file" | tail -1 | cut -d: -f1) "$file"
它更加简洁,因为它:
+
选项从给定的行打印到结尾,而不必计算从那里到结尾的距离。它通过引用$ file修复了一个错误(因此它适用于名称中包含空格的文件)。
答案 4 :(得分:3)
Sed的q
命令可以解决问题:
sed "/$pattern/q" $file
这将打印所有线条,直到它到达带有图案的线条。之后,sed将打印最后一行并退出。
答案 5 :(得分:1)
此问题的标题和说明不符。
对于问题的标题,@ David W.的答案为+1。也:
sed -ne '1,/PATTERN/p'
对于身体中的问题,你已经有了一些解决方案。
请注意,tac
可能特定于Linux。它似乎不存在于BSD或OSX中。如果您想要一个多平台的解决方案,请不要依赖于tac。
当然,几乎所有解决方案都要求您的数据在内存中假脱机,或者提交一次进行分析,第二次进行处理。例如:
#!/usr/local/bin/bash
tmpfile="/tmp/`basename $0`,$$"
trap "rm $tmpfile" 0 1 2 5
cat > $tmpfile
n=`awk '/PATTERN/{n=NR}END{print NR-n+1}' $tmpfile`
tail -$n $tmpfile
请注意,我对tail
的使用是针对FreeBSD的。如果您使用Linux,则可能需要tail -n $n $tmpfile
。
答案 6 :(得分:1)
Rob Davis向我指出你想要的 并不是你真正要求的:
你说:
我试图找到一个简洁的shell单行程序,它会在 文件中提供所有行,直到 某些模式。< / p>
但是在帖子的最后,你说:
但我还没有找到一种方法来打印 上次出现的所有内容。
我已经为您提供了first question的答案。这是第二个问题的一行答案:从正则表达式打印到文件末尾:
awk '{ if ($0 ~ /'"$pattern"'/) { flag = 1 } if (flag == 1) { print $0 } }' $file
一个类似的Perl单行:
export pattern="<regex>"
export file="<file>"
perl -ne '$flag=1 if /$ENV{pattern}/;print if $flag;' $file