BASH tail logfile and send mail on matched condition - bash

Completely clueless with Bash, but trying to learn to accomplish this single task.
I need a script that can tail the last 50 lines of a logfile, looking for the string "ERROR", and send an email if that condition is true.
I can do bits and pieces manually, but am not able to build something that works completely to stick in a cron. Sendmail works by itself.
tail -n 50 /var/log/unifi-video/recording.log | grep ERROR
works to at least output that. Ideally I'd like an email only if ERROR is found in the last 50 lines, else no action needs to be taken. If anyone can assist with the if/else statement to make this happen, it would be greatly appreciated.

If you are looking to do this in a one liner you could use something like:
[ $(tail -n 50 /var/log/unifi-video/recording.log | grep ERROR | wc -l) -gt 0 ] && yourmailcommand
This just pipes the output of your grep to wc -l which returns how many lines are returned from grep. If that count is greater than 0 then it will execute that bit after the double ampersands. If you want to include an else in the event that no ERROR lines are found you could:
[ $(tail -n 50 /var/log/unifi-video/recording.log | grep ERROR | wc -l) -gt 0 ] && yourmailcommand || dosomethingelseinstead

grep exits with status 0 if a match is found, 1 if not, so something like
tail -n 50 /var/log/unifi-video/recording.log | grep -q ERROR && <sendmail command here>
would do what you want. (The -q option suppresses any output, because you don't care what line actually matched, just that a match was found.)

Related

Search only last n lines of a file for matching text pattern and print filenames that do not have this pattern

I would like to search the last 2 lines of a bunch of similarly named files for a given text pattern and write out the filenames where matches are NOT found.
I tried this:
tail -n 2 slurm-* | grep -L "run complete"
As you can see "slurm" is the file base, and I want to find files where "run complete" is absent. However, this command does not give me any output. I tried the regular (non-inverse) problem:
tail -n 2 slurm-* | grep -H "run complete"
and I get a bunch of output (all the matches that are found but not the filenames):
(standard input):run complete
I figure that I have misunderstood how piping tail output to grep works, any help is greatly appreciated.
This should work -
for file in `ls slurm-*`;
do
res=`tail -n2 $file | grep "run complete" 1>/dev/null 2>&1; echo $?`;
if [ $res -ne 0 ];
then
echo $file ;
fi ;
done ;
Explanation -
"echo $?" gives us the return code of the grep command. If grep finds the pattern in the file, it returns 0. Otherwise the return code is non-zero.
We check for this non-zero return code, and only then, "echo" the file name. Since you have not mentioned whether the output of grep is necessary, I have discarded the STD_OUT and STD_ERR by sending it to /dev/null.

Grep qstat with a variable returns nothing

I am having trouble greping qstat with a variable
Scenario: script-A my wrapper submits another script-B as a job, inside script-B I am submitting other jobs in a loop(job1,job2..jobx), each of these jobs have different job-names which are stored in a variable $New_OS. I am trying to make my script-B sleep until all (Job1,job2..jobx) are completed.
Script:
Job_status=`qstat | grep "${New_OS}" | wc -l`
echo -e "\n\nJob_status: $Job_status"
while [ $Job_status -ne "0" ];
do
echo -e "Running PostProcessing for $Job_status sampleR, sleeping for 3 minutes..."
sleep 3m
Job_status=`qstat | grep "${New_OS}" | wc -l`
done;
Problem: The variable $Job_status which is supposed to return the number of jobs with a name $New_OS only returns 0 even though the jobs are still running.
Question: Why the grep of $New_OS in qstat is not returning the jobs with $New_OS as their names? I couldn't figure out the problem here, please suggest me a solution or a work around. Thanks in advance.
Finally adding '*' in my grep worked.
Job_status1=`qstat | grep ${New_OS}* | wc -l`
Expected grep to work without that '*', since I am not looking to 'grep -w' for an exact match. I am sharing it just in case if someone ends up in a similar situation. Thanks.
How about using following.
Job_status1=$(qstat | grep -c ${New_OS}*)
Since I don't have qstat in my system so couldn't check, it should work but, let me know how it goes then.

bash: pipe continuously into a grep

Not sure how to explain this but, what I am trying to achieve is this:
- tailing a file and grepping for a patter A
- then I want to pipe into another customGrepFunction where it matches pattern B, and if B matches echo something out. Need the customGrepFunction in order to do some other custom stuff.
The sticky part here is how to make the grepCustomFunction work here.In other words when only patternA matches echo the whole line and when both patterA & patternB match printout something custom:
when I only run:
tail -f file.log | grep patternA
I can see the pattenA rows are being printed/tailed however when I add the customGrepFunction nothing happens.
tail -f file.log | grep patternA | customGrepFunction
And the customGrepFunction should be available globally in my bin folder:
customGrepFunction(){
if grep patternB
then
echo "True"
fi
}
I have this setup however it doesn't do what I need it to do, it only echos True whenever I do Ctrl+C and exit the tailing.
What am I missing here?
Thanks
What's Going Wrong
The code: if grep patternB; then echo "true"; fi
...waits for grep patternB to exit, which will happen only when the input from tail -f file.log | grep patternA hits EOF. Since tail -f waits for new content forever, there will never be an EOF, so your if statement will never complete.
How To Fix It
Don't use grep on the inside of your function. Instead, process content line-by-line and use bash's native regex support:
customGrepFunction() {
while IFS= read -r line; do
if [[ $line =~ patternB ]]; then
echo "True"
fi
done
}
Next, make sure that grep isn't buffering content (if it were, then it would be written to your code only in big chunks, delaying until such a chunk is available). The means to do this varies by implementation, but with GNU grep, it would look like:
tail -f file.log | grep --line-buffered patternA | customGrepFunction

Error in script

I am new to bash and scripting and I am trying to create a simple script but for some reason it won't let me run this:
fileCount= ls -1 | wc -l
#echo $fileCount
for (( i=0; i<$fileCount; ++i )) ; do
echo item: $i
done
Whenever I try to run this it just gives me an error message saying it expected an operand.
I am really confused on the error here and any help would be greatly appreciated!
To get your code running with minimal change, replace:
fileCount= ls -1 | wc -l
With:
fileCount=$(ls -1 | wc -l)
$(...) is called command substitution. It is what you use when you want capture the output of a command in a variable.
It is very important that there be no spaces on either side of the equal sign.
Improvements
To speed up the result, use the -U option to turn off sorting.
To prevent any attempt to display special characters, use -q.
Thus:
fileCount=$(ls -1Uq | wc -l)
Lastly, when ls is writing to something other than a terminal, such as, in this command, a pipeline, it prints one file name per line. This makes -1 optional.
you missed to assign the output of wc -l to your variable. Try this:
fileCount=$(ls | wc -l)
(option "-1" is not needed, because ls writes one file per line if its stdout is not a terminal)

Error while exiting a while loop

I am monitoring a log file through a shell script and once a string matches i want to exit. i am using a while statement to read the logs file. But the problem is my script never exits it prints the string which i expect but never exits. below is my piece of script
tail -fn0 $TOMCAT_HOME/logs/catalina.out | \
while read line ; do
echo "$line" | grep "Starting ProtocolHandler"
if [ $? = 0 ]
then
exit
fi
done
Tried using grep -q but doesn't work out
Any help will be appreciated
You can just use grep -q:
tail -fn0 $TOMCAT_HOME/logs/catalina.out | grep -q "Starting ProtocolHandler"
It will exit immediately after 1st occurrence of string "Starting ProtocolHandler"
Are you just wanting the line number? What about grep -n? Then you don't even need a script.
-n, --line-number
Prefix each line of output with the line number within its input file.
Couple that with -m to get it to exit after 1 match
-m NUM, --max-count=NUM
Stop reading a file after NUM matching lines. If the input is standard input from a regular file, and NUM matching lines are output, grep ensures that the standard input is positioned to just after the last matching line before exiting, regardless of the presence of trailing context lines. This enables a calling process to resume a search. When grep stops after NUM matching lines, it outputs any trailing context lines. When the -c or --count option is also used, grep does not output a count greater than NUM. When the -v or --invert-match option is also used, grep stops after outputting NUM non-matching lines.
So, you'd have something like
grep -n -m 1 "Starting ProtocolHandler" [filename]
If you want it to exit:
cat [filename] | grep -n -m 1 "Starting ProtocolHandler"

Resources