@descendant_command: Just what do you think the command you posted is supposed to do?
@Kashif_Bash: Think about how the commands you are using work. "
grep -A20" prints out every occurrence of the matched line, plus the 20 lines following them. Then that output is sent into "
tail -n16", which filters out all but the last 16 lines.
What you really want is the
first 16 lines of the
last 21 lines of the output. So what we need to do is run it through a second filter,
head in this case.
Code:
grep -A 20 -e 'New USB device found' /var/log/messages | tail -n 21 | head -n 16 > usb_detail
However, that would still fail you if the matching string happens to be less than 21 lines from the end of the file. I think this will do you better:
Code:
tac /var/log/messages | grep -B16 -m1 'New USB device found' | tac > usb_detail
tac (
cat backwards) reverses the output of the file, so you filter from the last line up. then you
grep only the first instance (
-m1) of the string, and print it and the 16 lines before it (
-B), or as many as there actually are. Then you use
tac again to put it back in the correct order.
It may be possible to come up with something cleaner using
sed or
awk. I'll have to think about it a bit.
Edit: Here's a quickly-knocked-out awk expression that appears to do it, although it's a bit cumbersome. Likely grail or someone will come along and embarrass me with a much simpler version.
Code:
awk '/New USB device found/ { t=""; f=1 } ; { if ( f != 0 && f <= 17 ) { t=t"\n"$0 ; f++ } else{ f=0 }} END{ sub(/^\n/,"",t) ; print t }' /var/log/messages > usb_detail
Edit2: Whoops, I had to revise the code. It was printing an unwanted newline before the output. But I'm not sure how to remove it except to use a
sub function. I also decided to shorten the variables to one letter, to make it look a bit cleaner (t for "text" and f for "flag").