我有日志文件,其中只定期附加日期。我的日志文件如下所示:
Monday 2017
foo foo foo foo foo foo foo foo foo foo foo foo
foo foo foo ALARM foo foo foo foo foo foo foo foo
foo foo foo foo foo foo foo foo foo foo foo foo
foo foo foo foo foo foo ALARM foo foo foo foo foo
foo foo foo foo foo foo foo foo foo foo foo foo
我正在编写一个类似这样的脚本:
grep 'ALARM' myfile.log | tail -1
我需要搜索上一个警报上方的上一个日期条目,并将其包含在我的结果中。我不知道匹配的警报线上面会有多少行。
期望的输出:
Monday 2017
foo foo foo foo foo foo ALARM foo foo foo foo foo
答案 0 :(得分:2)
假设日期模式为Monday 2017
grep -E 'Monday 2017|ALARM' | grep -B1 'ALARM'
第二个grep是删除ALARM匹配之间的多个日期模式,
编辑:再次阅读问题似乎只有匹配ALARM的最后一行是通缉的,我会跟随perl一个班轮:perl -ne 'if(/Monday 2017/){$last_date=$_}if(/ALARM/){$date=$last_date;$line=$_}END{print $date,$line}' <<END
Monday 2017
foo foo foo foo foo foo foo foo foo foo foo foo
foo foo foo ALARM foo foo foo foo foo foo foo foo
foo foo foo foo foo foo foo foo foo foo foo foo
foo foo foo foo foo foo ALARM foo foo foo foo foo
foo foo foo foo foo foo foo foo foo foo foo foo
END
答案 1 :(得分:1)
您可以使用tac
逐行反转流(请参阅seq 10 | tac
查看其功能)。这是不便宜的,请注意,但如果你的东西足够小,这可以提供一个简单的解决方案:
grep -B 9999999 lastSearchTerm my.log | tac | grep -B 9999999 firstSearchTerm | tac
这会将块从firstSearchTerm打印到lastSearchTerm。
grep -B 9999999 lastSearchTerm my.log | tac | tail -n +2 | grep -m 1 lastBeforeTerm
这将只打印lastSearchTerm之前包含lastBeforeTerm的最后一行。
对于您的具体情况,应该这样做:
grep -B 9999999 ALARM my.log | tac | {
IFS= read -e line
grep -m 1 '2017'
echo "$line"
}
(调整2017
部分以匹配任何看起来像时间戳的行。)
当然,这不是最快的解决方案,但它很简单,适用于小输入。
答案 2 :(得分:1)
class SongDetail extends Component{
render(){
const { song, loading } = this.props.data;
console.log( "song details are...", this.props, "---", song );
if( loading ) return <div>loading...</div>; // loading = false when done loading and ready
return(
<div>
<Link to = "/">Back</Link>
<h3>{ song.title }</h3>
<LyricList lyrics = { song.lyrics }/>
<LyricCreate songId = { this.props.params.id }/>
</div>
);
}
}
+ Awk
解决方案:
示例tac
内容:
myfile.log
工作:
some text text text
Sunday 2017
foo foo foo foo foo foo foo foo foo foo foo foo
foo foo foo ALARM foo foo foo foo foo foo foo foo
foo foo foo foo foo foo foo foo foo foo foo foo
bar foo foo foo foo foo ALARM foo foo foo foo foo
bar foo foo foo foo foo foo foo foo foo foo foo
Monday 2017
foo foo foo foo foo foo foo foo foo foo foo foo
foo foo foo ALARM foo foo foo foo foo foo foo foo
foo foo foo foo foo foo foo foo foo foo foo foo
foo foo foo foo foo foo ALARM foo foo foo foo foo
text foo foo foo foo foo foo foo foo foo foo foo
awk '/ALARM/{ f=1 }f && /^[A-Z][a-z]+ 2[0-9]{3}/{ print; exit }' <(tac myfile.log)
- 反向打印文件行tac myfile.log
- 遇到/ALARM/{ f=1 }
行时 - 使用标记ALARM
f
- 指示“date”行/^[A-Z][a-z]+ 2[0-9]{3}/
- 打印当前行(作为结果行)并立即终止脚本执行输出:
print; exit
答案 3 :(得分:0)
这假设“日期”的特征是包含day
和四位数的行:
tac myfile.log \
| sed -En '/ALARM/,/day [[:digit:]]{4}/{/day [[:digit:]]{4}/{p;q}}'
与其他解决方案一样,它使用tac
反向打印行;然后sed命令执行此操作:
-n
默认禁止输出。
/ALARM/,/day [[:digit:]]{4}/ { # In the range from ALARM to the date
/day [[:digit:]]{4}/{ # On the line of the date
p # Print just that line
q # Exit
}
}
q
是在我们找到我们想要的内容之后避免阅读文件的其余部分。
请注意,某些seds可能需要额外的分号,如{p;q;}
。
答案 4 :(得分:0)
awk
解决方案,
awk 'NF==2 {d=$0}; /ALARM/ { printf("%s\n%s\n", d, $0)}' sample.txt
输出:
Monday 2017
foo foo foo ALARM foo foo foo foo foo foo foo foo
Monday 2017
foo foo foo foo foo foo ALARM foo foo foo foo foo
答案 5 :(得分:0)
我们不能用Grep有效地做到这一点。这是一个简单的Sed构造要记住:
sed -n '/before/ {h;n;}; /after/ {x;p;x;p;}' < input.txt
这将存储与模式before
匹配的最新行,然后在遇到与模式after
匹配的后续行时将其打印出来。然后,它也打印出匹配after
的行。要打破它:
-n
标志会抑制每一行的输出 - 我们会告诉Sed输出我们想要的内容。/before/
- 当我们找到与模式before
匹配的行时...
h
- 将其保存到保留空间缓冲区以供日后使用。n
- 继续下一行。/after/
- 当我们找到与模式after
匹配的行时...
x;p
- 使用保留缓冲区(before
)的内容交换该行并打印出来。x;p
- 将after
退回保留缓冲区并打印出来。这种运行速度非常快,因为我们可以在一次传递中过滤输入,而无需先输出管道或反转文件。
现在,让我们将其应用于问题中的示例:
sed -n '/^date pattern$/ {h;n;}; /ALARM/ {x;p;x;p;}' < input.txt
这只是将特定模式插入我上面描述的Sed程序中 - 它每次看到ALARM
时都会输出最近看到的日期和匹配的行。由于该问题只想在每个日期之后显示包含ALARM
的 last 行,因此我们需要稍微修改该程序:
sed -n '
/^date pattern$/ {
:alarm
x
/ALARM/ {s/^\(date pattern\)\n.*\n\(.*ALARM.*\)$/\1\n\2/;p;n;}
}
/ALARM/ H
$ b alarm
' < input.txt
而不是只保留日期行,这会缓冲日期和包含ALARM
的每一行,直到Sed遇到下一个日期,之后它将打印日期和保持缓冲区中的最后ALARM
行。我们检查是否存在ALARM
,因此我们不会在没有发生警报时打印日期。 :alarm
声明一个分支标签,我们可以使用b alarm
返回到文件的最后一行(用$
表示)来处理保留空间缓冲区中剩余的任何内容。 / p>
我在每个示例中都使用[A-Z][a-z]\+day [0-9]\{4\}
date pattern
,但会根据需要进行调整。
编辑:我想我误解了这个问题。看起来我们只想要整个文件中的最后一个日期和最后一个警报线。如果是这样,使用Tac首先反转文件会更快,但会消耗更多内存:
tac input.txt | sed -n '/ALARM/ {h;:a;n;/^date pattern$/ {p;x;p;q;}; ba;}'
使用这种方法,我们将最后一个警报存储在文件中,并在找到并打印文件中的最后一个日期后打印它。我们在找到最后一个日期后立即使用q
退出,以避免处理其余日期。如果我们的系统上没有Tac,我们也可以使用Sed来反转文件:
sed '1!G;h;$!d' < input.txt | sed ...