BASH better way to monitor files - bash

I've made a Bash script to monitor some server log files for certain data and my method probably isn't the most efficient.
One section specifically bugs me is that I have to write a newline to the monitored log so that the same line wont be read over continually.
Feedback would be greatly appreciated!
#!/bin/bash
serverlog=/home/skay/NewWorld/server.log
onlinefile=/home/skay/website/log/online.log
offlinefile=/home/skay/website/log/offline.log
index=0
# Creating the file
if [ ! -f "$onlinefile" ]; then
touch $onlinefile
echo "Name Date Time" >> "$onlinefile"
fi
if [ ! -f "$offlinefile" ]; then
touch $offlinefile
echo "Name Date Time" >> "$offlinefile"
fi
# Functions
function readfile {
# Login Variables
loginplayer=`tail -1 $serverlog | grep "[INFO]" | grep "joined the game" | awk '{print $4}'`
logintime=`tail -1 $serverlog | grep "[INFO]" | grep "joined the game" | awk '{print $2}'`
logindate=`tail -1 $serverlog | grep "[INFO]" | grep "joined the game" | awk '{print $1}'`
# Logout Variables
logoutplayer=`tail -1 $serverlog | grep "[INFO]" | grep "left the game" | awk '{print $4}'`
logouttime=`tail -1 $serverlog | grep "[INFO]" | grep "left the game" | awk '{print $2}'`
logoutdate=`tail -1 $serverlog | grep "[INFO]" | grep "left the game" | awk '{print $1}'`
# Check for Player Login
if [ ! -z "$loginplayer" ]; then
echo "$loginplayer $logindate $logintime" >> "$onlinefile"
echo "Player $loginplayer login detected" >> "$serverlog"
line=`grep -rne "$loginplayer" $offlinefile | cut -d':' -f1`
if [ "$line" > 1 ]; then
sed -i "$line"d $offlinefile
unset loginplayer
unset line
fi
fi
# Check for Player Logout
if [ ! -z "$logoutplayer" ]; then
echo "$logoutplayer $logoutdate $logouttime" >> "$offlinefile"
echo "Player $loginplayer logout detected" >> "$serverlog"
line=`grep -rne "$logoutplayer" $onlinefile | cut -d':' -f1`
if [ "$line" > 1 ]; then
sed -i "$line"d $onlinefile
unset logoutplayer
unset line
fi
fi
}
# Loop
while [ $index -lt 100 ]; do
readfile
done
Thanks!

instead of using multiple
tail -n 1 file
try the following construct:
tail -f file | while read line;do
echo "read: $line"
done
it will be much more reliable...and won't read the same line twice ;)
note: by using new processes of grep/awk/etc you are burning away processes...it's not that it is critical, but usually process creation is expensive...but if new lines occur rarely it's perfectly fine
where i want'ed to get is: if you are intrested, take a look at bash builting string manipulator function replace $(x/aa} ${x//aa} and friends..or try to use extended regexpes with grep

Related

BASH: Remove newline for multiple commands

I need some help . I want the result will be
UP:N%:N%
but the current result is
UP:N%
:N%
this is the code.
#!/bin/bash
UP=$(pgrep mysql | wc -l);
if [ "$UP" -ne 1 ];
then
echo -n "DOWN"
else
echo -n "UP:"
fi
df -hl | grep 'sda1' | awk ' {percent+=$5;} END{print percent"%"}'| column -t && echo -n ":"
top -bn2 | grep "Cpu(s)" | \sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | \awk 'END{print 100 - $1"%"}'
You can use command substitution in your first sentence (notice you're creating a subshell in this way):
echo -n $(df -hl | grep 'sda1' | awk ' {percent+=$5;} END{print percent"%"}'| column -t ):

bash script, ask for arg if not provided

I have created a script that will check to see if a user you provide is logged on and display the duration of the session if logged on. What i need to do now is if no argument (username) is provided when the command is issued, ask for one and have the same results as if you have provided one.
Here is what I have:
name=$(cat /etc/passwd | grep $1 | cut -d':' -f5 | tr ':' ' ' | sed 's/,//' | sed 's/^\([^ ]*\) \([^ ]*\)/\2 \1/' | sort -t' ' -k3,3)
terminal=$(who | grep $1 | cut -d' ' -f3)
loginHour=$(who | grep $1 | cut -c30-31)
loginMin=$(who | grep $1 | cut -c33-34)
loginMins=$((loginHour * 60 + loginMin))
nowHour=$(date +%R | cut -c1-2)
nowMin=$(date +%R | cut -c4-5)
nowMins=$((nowHour * 60 + nowMin))
totalMins=$((nowMins - loginMins))
hoursOn=$((totalMins / 60))
minsOn=$((totalMins % 60))
clear
echo
if [[ $# -eq 1 ]] ; then
grep -q $1 /etc/passwd
if grep -q $1 /etc/passwd ; then
clear
echo
if who | grep $1 > /dev/null ; then
echo "$name" is currently logged on to terminal "$terminal" and has been for "$hoursOn" hour"(s)" and "$minsOn" minute"(s)".
echo
exit 0
else
echo "$name" is NOT currently logged on.
echo
exit 1
fi
else
echo The user you entered is not a valid user on this system.
echo
exit 2
fi
fi
I had an attempt before but was not the desired result so I removed it out of confusion.
if [[ $# -eq 0 ]]
then
read -p "Enter Name: " username
else
username=$1
fi
then replace all subsequent references to $1 by $username
You can also abort if no name given
# : does nothing it just forces the evaluation
: ${1:?"Need to provide name to script"}

Variable loss in redirected bash while loop

I have the following code
for ip in $(ifconfig | awk -F ":" '/inet addr/{split($2,a," ");print a[1]}')
do
bytesin=0; bytesout=0;
while read line
do
if [[ $(echo ${line} | awk '{print $1}') == ${ip} ]]
then
increment=$(echo ${line} | awk '{print $4}')
bytesout=$((${bytesout} + ${increment}))
else
increment=$(echo ${line} | awk '{print $4}')
bytesin=$((${bytesin} + ${increment}))
fi
done < <(pmacct -s | grep ${ip})
echo "${ip} ${bytesin} ${bytesout}" >> /tmp/bwacct.txt
done
Which I would like to print the incremented values to bwacct.txt, but instead the file is full of zeroes:
91.227.223.66 0 0
91.227.221.126 0 0
127.0.0.1 0 0
My understanding of Bash is that a redirected for loop should preserve variables. What am I doing wrong?
First of all, simplify your script! Usually there are many better ways in bash. Also most of the time you can rely on pure bash solutions instead of running awk or other tools.
Then add some debbuging!
Here is a bit refactored script with debugging
#!/bin/bash
for ip in "$(ifconfig | grep -oP 'inet addr:\K[0-9.]+')"
do
bytesin=0
bytesout=0
while read -r line
do
read -r subIp _ _ increment _ <<< "$line"
if [[ $subIp == "$ip" ]]
then
((bytesout+=increment))
else
((bytesin+=increment))
fi
# some debugging
echo "line: $line"
echo "subIp: $subIp"
echo "bytesin: $bytesin"
echo "bytesout: $bytesout"
done <<< "$(pmacct -s | grep "$ip")"
echo "$ip $bytesin $bytesout" >> /tmp/bwacct.txt
done
Much clearer now, huh? :)

Bash error echo a command

I have a problem. I need to show a echo from a while, I use two echo the first one work but the second it give a error.
#!/bin/bash
conexiuni="/tmp/conexiuni"
if [ "$1" != "" ]; then
netstat -tuan | grep $1 | grep ESTAB | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -n > $conexiuni
else
netstat -tuan | grep ESTAB | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -n > $conexiuni
fi
cat $conexiuni | while read line
do
con=`echo ''$line'' | awk '{print $1}'`
ip=`echo ''$line'' | awk '{print $2}'`
if [ "$con" -gt "4" ]; then
`echo -e "$ip" >> /var/log/drop_sc_ip`
`echo -e "$ip"`
fi
done
if [ -f "$conexiuni" ];
then
`rm -rf $conexiuni`
fi
The error is :
./show_conn: line 15: 8.97.80.2: command not found
./show_conn: line 15: 8.76.109.13: command not found
./show_conn: line 15: 8.33.15.2: command not found
./show_conn: line 15: 9.118.226.3: command not found
You can write this part without the backticks:
if [ "$con" -gt "4" ]; then
echo -e "$ip" >> /var/log/drop_sc_ip
echo -e "$ip"
fi
also same in this part:
rm -rf $conexiuni
with the backticks, it first executes what is inside the backticks and then tries to execute the output of the backticks.
and change the loop:
while read con ip
do
if [ "$con" -gt "4" ]; then
echo -e "$ip" >> /var/log/drop_sc_ip
echo -e "$ip"
fi
done < $conexiuni

repeat function with different arguments if one variable within the function is empty

The function has to check if TARGET is empty, and if it is empty i want it to be repeated but this time with c1TIME=$hours:$minutes:[0-9][0-9]
then run it, and if it is still empty then with c1TIME=$hours:$c1minutes[0-9]:[0-9][0-9]
after that it should stop and echo some info.
$1 is a standard logfile with errors
$TIME string with time in this pattern HH:MM:SS
I am sorry if this is confusing, I'm appreciate any help
what this function does in detail:
first it takes a random time for example 12:34:56 and reads only
the "5" in c1seconds, then c1TIME is 12:34:5[0-9]
Target now tries to find a line where the string of c1TIME is located in the logfile and
prints the number of the line.
c1DATE changes the 12:34:5[0-9] in the found time like 12:34:56 for example.
the rest is actually unimportant
like I said above when TARGET is empty it has to run again, so how can i check TARGET while or after the function executed?
function check() {
c1seconds=`echo $TIME | awk '{print substr ($1,7,8)}' | head -1c`
c1TIME=$hours:$minutes:$c1seconds[0-9]
TARGET=`cat -n $1 | grep "$c1TIME" | awk '{printf "%s\n",$1}' | awk 'sub("$", "")'| head -1`
c1DATE=`cat $1 | grep "$c1TIME" | grep -Eo "[0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2}" | head -1`
EXCEPTION=`tail -$[$WINDOW_LINES-$TARGET] $1 | grep -c Exception`
ERROR=`tail -$[$WINDOW_LINES-$TARGET] $1 | grep -c Error`
SEMAPHORE=`tail -$[$WINDOW_LINES-$TARGET] $1| grep -c semaphore`
echo $TARGET
return 0
}
You want to repeat while target is empty: :) so here you go:
function check() {
c1seconds=`echo $TIME | awk '{print substr ($1,7,8)}' | head -1c`
c1TIME=$hours:$minutes:$c1seconds[0-9]
TARGET=`cat -n $1 | grep "$c1TIME" | awk '{printf "%s\n",$1}' | awk 'sub("$", "")'| head -1`
if [ x$TARGET = x ] ; then
c1TIME=$hours:$minutes:[0-9][0-9]
TARGET=`cat -n $1 | grep "$c1TIME" | awk '{printf "%s\n",$1}' | awk 'sub("$", "")'| head -1`
fi
c1DATE=`cat $1 | grep "$c1TIME" | grep -Eo "[0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2}" | head -1`
EXCEPTION=`tail -$[$WINDOW_LINES-$TARGET] $1 | grep -c Exception`
ERROR=`tail -$[$WINDOW_LINES-$TARGET] $1 | grep -c Error`
SEMAPHORE=`tail -$[$WINDOW_LINES-$TARGET] $1| grep -c semaphore`
echo $TARGET
}
BTW feels like an ugly way to do what you are doing :P
Feels even uglier :) but i don't know what you are trying to accomplish or what your input is so the answer is as it is above.
well you can always use recursion. keep a level=0 at the global level and call the function again.
level=0;
function check() {
Now instead of
c1TIME=$hours:$minutes:$c1seconds[0-9]
use
if [ $level -eq 0 ]; then c1TIME=$hours:$minutes:$c1seconds[0-9]; fi
if [ $level -eq 1 ]; then c1TIME=$hours:$minutes:[0-9][0-9]; fi
if [ $level -eq 2 ]; then c1TIME=$hours:$c1minutes[0-9]:[0-9][0-9]; fi
if [ $level -ge 2 ]; then echo "ERROR"; exit; fi
Now instead of
TARGET=`cat -n $1 | grep "$c1TIME" | awk '{printf "%s\n",$1}' | awk 'sub("$", "")'| head -1`
use
TARGET=`cat -n $1 | grep "$c1TIME" | awk '{printf "%s\n",$1}' | awk 'sub("$", "")'| head -1`
if [ "x$TARGET" = "x" ]; then ((level++)); check $#; fi
I have not run this so typos may be there, but main idea is as the number of recursion and checks are at max 3, use a simple if check with the depth of the recursion. You can modify the series of if to if else. Also if grep can open a file, why cat file and pipe it, and if awk can search for a pattern in a file, why cat file | grep pattern before it?

Resources