Count number of occurrences in file. conditional pattern matching - bash

Sorry if the title doesn't make it clear.
I have a file to be read every 15 mins and find a particular pattern in it (e.g. timeout). File does not have any fixed update frequency.
Outcome expected:-
1. if pattern is found 3 times in 15 mins, run command1.
2. if pattern is found 5 times in 15 mins, run command2.
File to be read from last read position for each check.
Thanks,
GRV

One way to do this is with a cron job. It is more complicated than other solutions but it is very reliable (even if your "check script" crashes, it will be called up again by cron after the period elapses). The cron could be:
*/15 * * * * env DISPLAY=:0 /folder/checkscript >/dev/null 2>&1
The env DISPLAY=:0 might not be needed in your case, or it might be needed, depending on your script (note: you might need to adapt this to your case, run echo $DISPLAY to find out your variable on the case).
Your "checkscript" could be:
#!/bin/bash
if [ -f /tmp/checkVarCount ]; then oldCheckVar="$(cat /tmp/checkVarCount)"; else oldCheckVar="0"; fi
checkVar="$(grep -o "pattern" /folder/file | wc -l)"
echo "$checkVar" > /tmp/checkVarCount
checkVar="$(($checkVar-$oldCheckVar))"
if [ "$checkVar" -eq 3 ]; then command1; fi
if [ "$checkVar" -eq 5 ]; then command2; fi
exit
It is not included on your question, but if you meant to run the commands as well if the pattern is found 4 times or 7 times, then you could change the relevant parts above to:
if [ "$checkVar" -ge 3 ] && [ "$checkVar" -lt 5 ]; then command1; fi
if [ "$checkVar" -ge 5 ]; then command2; fi

Related

ash: -c: unknown operand

I'm trying to run a ash script that constantly checks how many characters are in a file and execute some code if it reaches at least 170 characters or if 5 seconds have passed. To do that I wanted to call wc -c, however it keeps telling me it has an unknown operand.
The code:
#!/bin/ash
while true; do
secs=5
endTime=$(( $(date +%s) + secs ))
while [ /usr/bin/wc -c < "/tmp/regfile_2" -gt 170 ] || [ $(date +%s) -lt $endTime ]; do
#more code
It's output is ash: -c: unknown operand
You want to check whether the output from wc meets a specific condition. To do that, you need to actually execute wc, just like you already do with date to examine its output.
while [ $(wc -c < "/tmp/regfile_2") -gt 170 ] ||
[ $(date +%s) -lt $endTime ]; do
# ...stuff
Notice the $(command substitution) around the wc command.
As you can see from the answer to the proposed duplicate Checking the success of a command in a bash `if [ .. ]` statement your current command basically checks whether the static string /usr/bin/wc is non-empty; the -c after this string is indeed unexpected, and invalid syntax.
(It is unclear why you hardcode the path to wc; probably just make sure your PATH is correct before running this script. There are situations where you do want to hardcode paths, but I'm guessing this isn't one of them; if it is, you should probably hardcode the path to date, too.)

Error in while loop for checking the file existence only for a fixed time

I need to write a while loop to check for a file existence.
My requirement is: check for the file only for 5 minutes. If file come in that path within 5 minutes exit the loop and continue rest of the script otherwise exit from the script after 5 minutes with an error 'file not found'. I wrote the code like this :
SOURCEFILE=/path/*file.csv
StartTime=$(date +'%s')
TimeSpan=300
EndTime=$((StartTime + TimeSpan))
while [[ ! -f ${SOURCEFILE} && $(date +'%s') < ${EndTime} ]]
do
echo "inside loop"
sleep 25
done
echo "outside loop"
But with this while loop, even if the file is present in the mentioned path, it is going inside loop and will exit only after 300 seconds. I am beginner in shell scripting and I am not able to understand the issue. I am using ksh.
I could also tell you that it works find with while [ ! -f {SOURCEFILE} ] only. But whenever I add any && condition to while loop , then the -f is not working properly.
The SOURCEFILE=/path/*file.csv is wrong in your case. It can't be evaluated right with the -f flag.
An easy solution would be to use find or ls and count the result:
find /path/ -name "*file.csv" -type f
# then count the result...
Now I think there is a logic issue with the operators precedence. To force evaluation of the ! for the -f only, use parenthesis. Here is what works for me, and you must adapt it a little to match the * before file.csv:
while [[ ( ! -f file.csv ) && $(date +'%s') < ${EndTime} ]]
do
echo "inside loop"
sleep 25
...
There are some more explanation on this answer. The "and" operator precedes the "not", that's why you had the issue.
Your primary issue is getting the asterisk (*) to expand at the 'right time'.
It doesn't help that the [ ] and [[ ]] constructs behave differently, especially when it comes to if/when to expand that asterisk. [You can peruse the google search for 'ksh single bracket vs double bracket' for more details.]
Try running the following to see the differences between single/double brackets and unquoted/single-quoted/double-quoted variable:
SOURCEFILE=/path/*file.csv
set -x
[ ! -f ${SOURCEFILE} ] && echo 'missing'
[ ! -f '${SOURCEFILE}' ] && echo 'missing'
[ ! -f "${SOURCEFILE}" ] && echo 'missing'
[[ ! -f ${SOURCEFILE} ]] && echo 'missing'
[[ ! -f '${SOURCEFILE}' ]] && echo 'missing'
[[ ! -f "${SOURCEFILE}" ]] && echo 'missing'
NOTE: Notice which tests expand the asterisk and which are looking for a (literal) asterisk in the name.
NOTE: Try adding a space to your file name (eg, *file XX.csv) and run the above tests ... tricky, tricky, tricky ...
For this particular case ... asterisk/wildcard in file name, no spaces, ksh ... you'll likely be ok with something like:
while [[ ! -f ${SOURCEFILE} ]] && [[ $(date +'%s') < ${EndTime} ]]

Illegal number in shell script

I am new to writing scripts and am trying to start out with a simple one. I am stumped as to why I am receiving the error: [: 13: Illegal number: count from the code below. Line 13 is the last fi.
count=grep "^$(date -d -30minute +'%Y-%m-%d %H:%M')" /var/log/****/zlsapp.log | wc -l
if [ count -ge 50 ]
then
if [ count -lt 100 ]
then
exit 1
fi
if [ count -ge 100 ]
then
exit 2
fi
exit 0
fi
Also is there anyway to do compound if statements like if(count >= 50 && count < 100)?
Two reasons. (1) in bash variables are untyped (could be int, could be char). In order to remove ambiguity, you can specify the type with:
declare -i count
To tell bash it should be an int. (2) you need to dereference your variables with $ to get the number back. I.E.
[ $count -lt 100 ]
(it is also good practice to quote your variables - not required, but good practice: [ "$count" -lt 100 ]. Drop a comment if you still have trouble.
The line:
count=grep "^$(date -d -30minute +'%Y-%m-%d %H:%M')" /var/log/zumigo/zlsapp.log | wc -l
probably does not do what you think it does (or you've not accurately copied and pasted your actual code into the question). If run in the middle of 2014-08-01, it runs the command "2014-08-01 12:00" with the log file as an argument and the environment variable count set to the value grep, and pipes the output from the probably non-existent command to wc -l.
When you subsequently go to test $count in the test statements, it is an empty string, which doesn't convert properly to a number.
You probably meant:
count=$(grep "^$(date -d -30minute +'%Y-%m-%d %H:%M')" /var/log/zumigo/zlsapp.log | wc -l)
This captures the output of running grep on the log file and piping the output to wc -l.
If you run your script with bash -x or equivalent (the -x option usually shows the execution of the script), you should see this.
The problem is that count does not refer to the variable count; it's simply the string "count".
Change:
if [ count -ge 50 ]
to
if [ $count -ge 50 ]
and make the corresponding change elsewhere (but not in the initial assignment).
You should also use double quotes:
if [ "$count" -ge 50 ]
Also is there anyway to do compound if statements like if(count >= 50 && count < 100)?
Yes:
if [ "$count" -ge 50 -a "$count" -lt 100 ]
is likely to work, but the -a (logical and) operator is marked as obsolescent by POSIX. Instead write
if [ "$count" -ge 50 ] && [ "$count" -lt 100 ]
If you're using bash, info bash and search for the "test" command ([ is an alias for test).
And if you're using bash, you should consider using [[ ... ]] rather than [ ... ] -- or you can use (( ... )) for arithmetic expressions. See the bash documentation for more information (follow the iink or type info bash).
In addition to the missing $ signs, the first line of your script:
count=grep "^$(date -d -30minute +'%Y-%m-%d %H:%M')" /var/log/****/zlsapp.log | wc -l
doesn't set the count variable, since you're not capturing the output of the grep ... | wc -l command. To do so:
count=$(grep "^$(date -d -30minute +'%Y-%m-%d %H:%M')" /var/log/****/zlsapp.log | wc -l)
(Yes, $(...) can be nested.)

Removing files in Unix using bash

I'm trying to delete a large amount of files from my computer, and I'm trying to write a bash script to do so using the rm command. What I want to know is how to do equality in bash, and why my code (posted below) won't compile. Thank you for your help!
#!/bin/bash
# int-or-string.sh
b="0000"
c="linorm"
f=500
e1=2
e2=20
e3=200
e4=2000
for i in {0..10000}
do
a=$(($f*$i))
if ["$i" -eq "$e1"]
then
b="000"
echo $b$
fi
if ["$i" -eq "$e2"]
then
b='00'
fi
if ["$i" -eq "$e3"]
then
b='0'
fi
if ["$i" -eq "$e4"]
then
b =''
fi
if [bash$ expr "$i" % "$e3$ -ne 0]
then
d = $b$c$a
rm d
fi
done
Shell scripts aren't compiled at all.
You need spaces after your [ and before your ].
if [ "$i" -eq "$e1" ]
There's an errant bash$ in there you probably don't want at all. It should probably be a $() operator:
if [ $(expr "$i" % "$e3") -ne 0 ]
You can't have spaces around the = in bash. For example, change b ='' to b='' and d = $b$c$a to d=$b$c$a.
echo $b$ looks like it should be echo $b.
Shell script does not compile it is a scripting language.
Try to fix this line :
if [bash$ expr "$i" % "$e3$ -ne 0]
Make it like below :
if [ $(expr "$i" % "$e3$") -ne 0 ]
You need spaces around the square brackets. The [ is actually a command, and like all commands needs to be delineated by white space.
When you set values for variables in shell, you do not put spaces around the equals signs.
Use quotation marks when doing comparisons and setting values to help delineate your values.
What happens if none of the if conditions are true, and $b isn't set.
What is the logic behind this code. It seems to be a bunch of random stuff. You're incrementing $ from 1 to 10000, but only setting the value of $b on only four of those values. Every 200 steps, you delete a file, but $b may or may not be set even though it's part of the file name.
Did you write this program yourself? Did you try to run it? What errors were you getting? Did you look at the lines referenced by those errors. It looks like you included the bash$ prompt as part of the command.
There were plenty of errors, and I've cleaned most of them up. The cleaned up code is posted below, but it still doesn't mean it will do what you want. All you said is you want to delete "a large amount of files" on your computer, but gave no other criteria. You also said "What I want to know is how to do equality in bash" which is not the question you stated in you header.
Here's the code. Note the changes, and it might lead to whatever answer you were looking for.
#!/bin/bash
# int-or-string.sh
b="0000"
c="linorm"
f=500
e1=2
e2=20
e3=200
e4=2000
for i in {0..10000}
do
a=$(($f*$i))
if [ "$i" -eq "$e1" ]
then
b="000"
elif [ "$i" -eq "$e2" ]
then
b='00'
elif [ "$i" -eq "$e3" ]
then
b='0'
elif [ "$i" -eq "$e4" ]
then
b=''
fi
if ! $(($i % $e3))
then
d="$b$c$a"
rm "$d"
fi
done
ERRORS:
Spaces around the [ and ]
The rm "$d" command was originallyrm dwhich would just remove a file namedd`.
if/then statement converted to if/else if.
Rewrote [ $(expr "$1" % "$e3") -ne 0 ].
No need for expr since BASH has $((..)) syntax.
No need for test command ([) since if automatically evaluates zero to true and non-zero to false.
Added quotes.

Bash on Windows while loop

I am attempting to port a shell script to run on GNU for Windows, which provides a bash shell for Windows. I've run into an issue:
while [ $no -le $number ]
doesn't seem to register. As a test, I tried
no=1
number=10
echo debug1
while [ $no -le $number ]
do
echo debug2
no=$((no+1))
done
echo debug3
This returns debug1 and debug3, so it doesn't seem to even enter the loop.
no=1
echo debug1
while [ $no -le 10 ]
do
echo debug2
no=$((no+1))
done
echo debug3
This, on the other hand, works, and gives me debug1, 10 debug2s, and debug3.
I'm guessing it's a syntax error, but I'm not sure how to fix this. Any suggestions?
That works fine (as it should) under bash in Linux, and even under the relatively ancient bash I have in CygWin:
pax> number=10
pax> no=1 ; while [ $no -le $number ] ; do echo $no ; no=$((no+1)) ; done
1
2
3
4
5
6
7
8
9
10
My only suggestion is to print out the two variables before you attempt to enter the while loop, and after the no=$((no+1)) line, in case there's something wrong, something like:
echo "[$no] [$number]"
You may also want to put a set -x at the top of your script as it will output each interpreted line before trying to execute it.

Resources