6
\$\begingroup\$

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
rolfl
98.1k17 gold badges219 silver badges419 bronze badges
asked Jul 19, 2013 at 20:39
\$\endgroup\$
1
  • \$\begingroup\$ A minor remark: I think ! -z is equivalent to -n. \$\endgroup\$ Commented Jul 19, 2013 at 21:37

2 Answers 2

4
\$\begingroup\$

Your goal is to monitor $serverlog continuously, and update $onlinefile and $offlinefile accordingly. The fact that you repeatedly close and reopen $serverlog is problematic, not only for performance reasons, but as you remarked, you risk processing the same line endlessly. Therefore, your general strategy should be to keep the file open, like this:

tail -f "$serverlog" | do_all_processing_here

Note that grep "[INFO]" doesn't do what you intend; instead, it matches lines that contain any of the characters I, N, F, or O. You probably meant grep -F '[INFO]' — the -F causes grep to treat your pattern as a fixed string rather than a regular expression. Then, your structure would be:

tail -f "$serverlog" | grep -F '[INFO]' | do_more_processing_here

I'm going to guess that [INFO] would appear in the third field of your server log. If so, the efficient solution would be...

tail -f "$serverlog" | \
while read date time severity player message ; do
 case "$severity" in
 \[INFO\])
 case "$message" in
 *joined the game*)
 echo "$player $date $time" >> "$onlinefile"
 sed -i -e "$(
 awk -v player="$player" '1ドル == player { print NR "d;"}' "$offlinefile"
 )" "$offlinefile"
 ;;
 *left the game*)
 echo "$player $date $time" >> "$offlinefile"
 sed -i -e "$(
 awk -v player="$player" '1ドル == player { print NR "d;"}' "$onlinefile"
 )" "$onlinefile"
 ;;
 esac # case $message
 ;;
 esac # case $severity
done

It's also possible to skip awk and edit $offlinefile and $onlinefile directly with

sed -i "/^$player /d" "$offlinefile"

but there could be a vulnerability if $player contained special characters such as .*. A better solution, if you have GNU awk ≥ 4.1.0, is

awk -v player="$player" '1ドル != player' -i inplace "$offlinefile"

With other versions of awk, you could use tempfile to help you perform the edit.

answered Aug 19, 2013 at 17:39
\$\endgroup\$
2
\$\begingroup\$

I have small suggestions for the function readfile to reduce the number of shell commands you need to run :

  1. Save the output of the following into a variable so it can be reused as shown in #2:

    last_line=$(tail -1 $serverlog | grep "[INFO]")
    
  2. As you are already using awk, use it to search and select columns both:

    login_time=$(echo $last_line | awk '/joined the game/ {print 2ドル}')
    
  3. Since you are searching in a single file and not recursively, the -r flag to grep can be removed.

answered Jul 20, 2013 at 11:15
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.