Hi. I have a unix script 'if' condition that greps through a file and searches for the strings ERROR and WARNING.
if egrep -q 'ERROR|WARNING' myfile.txt; then
When this 'if' condition is true then I want to be able to capture 10 lines before the string and 10 lines after the string. So a total of only 20 lines.
Can anyone help me with a way to grep through a files and then output only 20 lines of the file (10 before the string - 10 after) that surround the string when it's found?
Getting lines after it is doable since grep has a special option to help with that, -m. That will cause it to stop reading after the number of matches you tell it.
#!/bin/sh
# open the file
exec 5<filename
# Read up to the first match.
# because of -m, grep will leave the open file on the line after it.
# Then print 10 more lines.
( grep -m 1 "MATCH" && head -n 10 ) <&5
# close the file
exec 5<&-
Getting the lines before it is much harder, since a shell can't seek backwards.
---------- Post updated at 11:18 AM ---------- Previous update was at 11:17 AM ----------
You never said what your system was. That's a GNU option and I guess you don't have GNU.
Yes, it means you don't have GNU grep, that's usually found in Linux, though occasionally installed elsewhere as a third-party app for its convenient options. Here's a brute-force non-GNU solution.
#!/bin/ksh
TMP="/tmp/$$"
while read LINE
do
if [[ "${LINE}" == *ERROR* ]] || [ "${LINE}" == *WARNING* ]]
then
tail -n 10 "$TMP"
echo "$LINE"
head -n 10
: > "$TMP"
else
echo "$LINE" >> "$TMP"
fi
done < inputfile
rm -f "$TMP"
It's not perfect. If more than one error happens within the 10 lines it might not capture all the context.
---------- Post updated at 11:47 AM ---------- Previous update was at 11:32 AM ----------
#!/bin/ksh
TMP="/tmp/$$"
CONTEXT=0
while read LINE
do
if [[ "${LINE}" == *ERROR* ]] || [ "${LINE}" == *WARNING* ]]
then
[[ "${CONTEXT}" -le 0 ]] && tail -n 10 "$TMP"
CONTEXT=10
fi
if [[ "${CONTEXT}" -gt 0 ]]
then
echo "${LINE}"
fi
echo "${LINE}" >> "$TMP"
((CONTEXT--))
done < inputfile
rm -f "$TMP"
This one should make better output, but is slower.