improve / fine-tune a Shell script ( tail command?) so that it consumes less CPU

When I run a Shell script (codes are given below) for 10 seconds (timed
using my watch), it consumes the following amount of CPU resource :

/usr/local/bin > time ./chkXXXXcount.sh
real        6.8
user        1.1
sys         2.6

I suspect it's the tail command in the script but I'm not sure.
Need someone to fine-tune my codes below so that it consumes
less CPU.  Running the script using "nohup nice ./chkXXXXcount.sh &"
is not an option.   The codes of the script follows :


c2=5
c3=1

while [ 1 ]
do
  c1=`tail -1 /app/bea/yyy/eservices/XXXX/countlog/Xppt2activecount_ManagedXpptServer.log | sed 's/=/ /' | awk '{print ($3)}'`
  # echo $c1
   # compare c1 and c2 so tt if same as last value, we dont alert again
   if [ "$c1" -ge "250" ] && [ "$c1" -ne "$c2" ]
   then
     cd /app/bea/yyy/eservices/XXXX/countlog
     tail -5 Xppt2activecount_ManagedXpptServer.log |sed s/ConcurrSessCnt/Cnt/ | ux2dos > /home/myuser/Xppt.txt
     echo $c1 > /home/myuser/Xpptcnt.dat  # this file is for SMS & Xppt.txt above is for email

   if [ "$c3" -eq "1" ]
   then
     DT1=$(date +%d%H%M)
   fi

   if [ "$c3" -eq "5" ]
   then
     DT5=$(date +%d%H%M)
   fi

   chmod 777 /home/myuser/Xpptcnt.dat
   chmod 777 /home/myuser/Xppt.txt
   /var/tmp/Xppt/sendmlcnt.pl

   sleep 9  # about 3 times the frequency of job in emsyyy01
   rm -f /home/myuser/Xppt.txt
   rm -f /home/myuser/Xpptcnt.dat

   # if  clauses below r to ensure we dont get too many emails/SMS ie
   #  pause for 2 mins if get 3 consecutive alerts within approx 1 min or so
     if [ "$c3" -ge "3" ]
       then
          if [ "$DT1" -eq "$DT5" ]
          then
             sleep 120
             c3=0
          fi
     fi

   c3=$((c3 +1))

   fi

   c2=c1

done


I'm running on HP-UX B11.11
sunhuxAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

ozoCommented:
is Xppt2activecount_ManagedXpptServer.log  a very large file?
0
sunhuxAuthor Commented:

Generally about 50kbytes to 85kbytes with about 950 to 1980 lines
0
sunhuxAuthor Commented:

I'll need to run the script continuously using nohup in background
to check the last line of that Xppt2activecount_ManagedXpptServer.log
file to get the count & if it exceeds certain value, I have to alert.

After going thru the historical data, I can see that this
Xppt2activecount_ManagedXpptServer.log can be updated
every 8 seconds or even 60 seconds
0
Build an E-Commerce Site with Angular 5

Learn how to build an E-Commerce site with Angular 5, a JavaScript framework used by developers to build web, desktop, and mobile applications.

sunhuxAuthor Commented:

Extracted below a few lines from that count log file:

2011-03-09 23:55:40,886;ConcurrSessCnt=37
2011-03-09 23:56:08,498;ConcurrSessCnt=37
2011-03-09 23:57:54,480;ConcurrSessCnt=36
2011-03-09 23:57:59,166;ConcurrSessCnt=37
2011-03-09 23:58:02,435;ConcurrSessCnt=37
2011-03-09 23:58:09,584;ConcurrSessCnt=37
2011-03-09 23:58:40,996;ConcurrSessCnt=38
2011-03-09 23:59:30,815;ConcurrSessCnt=34
2011-03-09 23:59:39,885;ConcurrSessCnt=35
0
TintinCommented:
The time figures you posted aren't possible for the code you posted as the code has an infinite loop.

What conditions are you using the time the script?

BTW, easier to read with proper indentation and use the "Code" formatting in EE, eg:

0
sunhuxAuthor Commented:
c2=5
c3=1

while [ 1 ]
do
  c1=`tail -1 /app/bea/yyy/eservices/XXXX/countlog/Xppt2activecount_ManagedXpptServer.log | sed 's/=/ /' | awk '{print ($3)}'`
  # echo $c1
   # compare c1 and c2 so tt if same as last value, we dont alert again
   if [ "$c1" -ge "250" ] && [ "$c1" -ne "$c2" ]
   then
     cd /app/bea/yyy/eservices/XXXX/countlog
     tail -5 Xppt2activecount_ManagedXpptServer.log |sed s/ConcurrSessCnt/Cnt/ | ux2dos > /home/myuser/Xppt.txt
     echo $c1 > /home/myuser/Xpptcnt.dat  # this file is for SMS & Xppt.txt above is for email

   if [ "$c3" -eq "1" ]
   then
     DT1=$(date +%d%H%M)
   fi

   if [ "$c3" -eq "5" ]
   then
     DT5=$(date +%d%H%M)
   fi

   chmod 777 /home/myuser/Xpptcnt.dat
   chmod 777 /home/myuser/Xppt.txt
   /var/tmp/Xppt/sendmlcnt.pl

   sleep 9  # about 3 times the frequency of job in emsyyy01
   rm -f /home/myuser/Xppt.txt
   rm -f /home/myuser/Xpptcnt.dat

   # if  clauses below r to ensure we dont get too many emails/SMS ie
   #  pause for 2 mins if get 3 consecutive alerts within approx 1 min or so
     if [ "$c3" -ge "3" ]
       then
          if [ "$DT1" -eq "$DT5" ]
          then
             sleep 120
             c3=0
          fi
     fi

   c3=$((c3 +1))

   fi

   c2=c1

done

Open in new window


Inserted the script using EE "Code" format.

> What conditions are you using the time the script?
I login as root when the server's CPU utilization is about
50-80%.

Not sure if I've replied to what you wanted
0
TintinCommented:
What I meant was that your script has an infinite loop, so it would only finish if you interrupted it, so I have no idea how you got the original timings.

Additionally, your script has various conditions which will affect the timings and the amount of CPU consumed, so without knowing what data you ran your test with, it's hard to say what can be improved.
0
tfewsterCommented:
Your script has no delay in it for when things are "normal" [see below for the "normal" path through the script], even if the log count log hasn't changed, so it will loop as fast as it can and use as much CPU as it can.

Put a "sleep 1" before the "done", if the count doesn't change that quickly

BTW, the "sed" call can be made redundant if you use `awk -F "=" '{print ($3)}'`
That will cut out one external program call and improve efficiency (Though it's barely worth it)


while [ 1 ]
do
  c1=`tail -1 /app/bea/yyy/eservices/XXXX/countlog/Xppt2activecount_ManagedXpptServer.log | sed 's/=/ /' | awk  '{print ($3)}'`

# "if" statement taken out, as it's only for when thing go wrong ;-)

   c2=c1

done

Open in new window

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
TomuniqueSr. AIX AdminCommented:
Also, you can combine the two "chmod" and "rm" lines to 1, that should save a microsecond or so..

Tail  does a seek to the end of the file and starts working backward.. it doesn't read the entire file..
and for 80K or whatever.. that's not the issue in any delays.

what will really help your app avoid burning unneeded cpu cycles, is avoid re-reading the file if your input data hasn't changed.
Pipe the output of a tail -f into a loop.. so you're code only executes if there's a new record.   this avoid needing to determine how long to sleep and not miss records etc... and you're guaranteed to see every record.  even if a flood of records get appended to your log.

cd /app/bea/yyy/eservices/XXXX/countlog
tail -1 -f Server.log |awk -F= '{print $2}'| while read c1
do
    code
done
0
sunhuxAuthor Commented:
Hi Tintin,

>What I meant was that your script has an infinite loop, so it would only finish
>if you interrupted it, so I have no idea how you got the original timings.
Yes, I used a watched to time it for 10 secs & then press Ctrl-C to interrupt it


Hi tfewster,

So your code would be

while [ 1 ]
do
  c1=`tail -1 /app/bea/yyy/eservices/XXXX/countlog/Xppt2activecount_ManagedXpptServer.log | sed 's/=/ /' | awk  '{print ($3)}'`

# "if" statement taken out, as it's only for when thing go wrong ;-)

   c2=c1; sleep 1

done




Hi Tomunique,

I'll test out your code.  One clarification on your codes :
Wouldn't the "-f" cause tail to read the end of the file
continuously ?
  tail -1 -f Server.log |awk -F= '{print $2}'| while read c1
0
tfewsterCommented:
Hi sunhux - Yes, that's what I meant [I just removed the "if" statements to make it easier to read the logic of the script], but Tomunique's suggestion of `tail -f` is far better, even if you can't use "-1" as well.
0
TomuniqueSr. AIX AdminCommented:
tail -f
  does not read continuously.  It monitors the inode to determine a growth in size.(about once a second).  If the file grows, it streams the new data to stdout.  
0
sunhuxAuthor Commented:

Thanks for clarifying tfewster & Tommunique.

Will try out Tommunique's method tomorrow morning.
0
sunhuxAuthor Commented:
Hi Tommunique,

I ran 2 sessions at the same time & when the value changes (as
shown in Session 1, value changes fr 47 to 48),  the codes in
Session 2 (which closely match your codes) did not execute :

Session 1:
========
cd /app/bea/yyy/eservices/XXXX/countlog
tail -1 -f xxxServer.log
2011-04-01 10:52:21,058;ConcurrSessCnt=47
2011-04-01 10:53:00,026;ConcurrSessCnt=48


Session 2:
========
cd /app/bea/yyy/eservices/XXXX/countlog
tail -1 -f xxxServer.log |awk -F= '{print $2}'| while read c1
do
   echo $c1
done

What did I miss?
0
sunhuxAuthor Commented:

To clarify myself, for Session 2,
  the line  "echo $c1 "
did not echo out the output
0
sunhuxAuthor Commented:

& I noticed Session 2 can't be interrupted by Ctrl-C while Session 1 can be interrupted.

Also, the following command would regularly output something to screen, so
I guess the " | while read c1 " portion has an issue

# tail -1 -f ippt2activecount_ManagedIpptServer.log | awk -F= '{print $2}'
34
36
38
0
TomuniqueSr. AIX AdminCommented:
Hmm, we have an 11.11 system I can test on, maybe there's something about hpux's implementation.  Let me look
0
sunhuxAuthor Commented:

Hi Tomunique,

Have you got any update on testing it on your  HP-UX B11.11 ?
0
tfewsterCommented:
It may be an HP-UX implementation issue with `tail`; You could try
  tail -n -1 -f
or just
  tail -f    # The code will process the last few lines of the file correctly, one by one
0
TomuniqueSr. AIX AdminCommented:
the problem would be tail if his testing shows that it does return data when updated.   He's stating that the while loop isn't triggering.

I just ran the following code..
$ echo abc > x; echo def >> x; echo ghi >> x; echo jkl >> x
$ C=0
$ tail -1 -f x | while read line
do
  (( C = C + 1 ))
   echo $C $line
done

From another window, I echo'd mno and pqr to x and got the following output:
1 jkl
2 mno
3 pqr

So, in short, I'm not sure what's going on with your while loop, other than to ask you to check it with "cat -vet" to check for any unprintable characters in there messing with the code.

Are you saying it didn't echo ANYTHING?  Or just didn't pick up the updates?

0
TomuniqueSr. AIX AdminCommented:
correction to comment above...
first sentence should read:
the problem would NOT be tail if his testing shows that it does return data when updated.
0
sunhuxAuthor Commented:

> Are you saying it didn't echo ANYTHING?
It didn't echo anything.


On my HP-UX, the following code echoed the appended  :
# tail -1 -f ippt2activecount_ManagedIpptServer.log | awk -F= '{print $2}'

but the following code did not echo anything & just 'hanged' there (& can't
be interrupted with Ctrl-C ) :
tail -1 -f ippt2activecount_ManagedIpptServer.log | awk -F= '{print $2}' | while read c1
do
   echo $c1
done


I'll check for hidden characters with "cat -ev" (or rather if the TERM variable is set
correctly) but the above codes are all typed on the terminal directly, not run from
a script.  I'll run from a script tomorrow to see if it makes any difference
0
TomuniqueSr. AIX AdminCommented:
shouldn't make a difference script or cmdline... (unless that translates into a different shell being used).

Ok, please try this.   I may know what's happening (although you should get at least 1 record out).
create a test file as I did   (about 10 lines of junk)

let's run the while cmd without tail
cat TESTFILE | while read c2
do
     echo $c2
done

(this can also be coded
while read c2
do
   echo $c2
done   <  TESTFILE

I bet both of the above will work fine...
------
Ok, now a test to isolate the problem with tail
we need to test if the inode is changing.  If tail is monitoring an "old" inode because things are renamed, it will hang.

use the -i  parm to ls.  this will show the inode number of the file in column 1.
ls -i -l  ippt2activecount_ManagedIpptServer.log   make note of column 1 (inode#) and col 6 (size)

while you're running your while loop (as we originally discussed using tail)
in a different window, run the ls -i periodically and watch the inode number.
If the number changes, or the file shrinks, then this solution using tail -f won't work. (I can't imagine a log shrinking, but, maybe the way the app is doing log management).

If the inode number is the same, and the size keeps growing, then tail -f should work fine.
If your files get swapped around a lot, then tail ends up watching the wrong file, and hangs (it sucks at the shell game, pardon the pun)..

Tom



0
sunhuxAuthor Commented:

Thanks very much.  

The app is written by a remote programmer, sort of weblogic related.

I'll test it tomorrow as I don't have remote/VPN access to the HP-UX server.
0
sunhuxAuthor Commented:
I've checked with "ls -i -l xxxServer.log" & noted that the
first column (inode) did not change despite that I appened
additional line of data to it.

I tried with another input log file (apend.txt), just in case the app created
'weird' problem with that logfile but I still get the same issue
of "hanged" & nothing was echoed :

# ls -i -l /tmp/apend.txt
   329 -rw-r--r--   1 wlsuser    wlsgroup        42 Apr  4 10:31 /tmp/apend.txt
# tail -1 xxxServer.log >> /tmp/apend.txt                
# ls -i -l /tmp/apend.txt
   329 -rw-r--r--   1 wlsuser    wlsgroup        84 Apr  4 11:29 /tmp/apend.txt

Appears to hv to do with "while" but strange that using
cat with while is Ok :

  cat apend.txt | while read c1
  do
      echo $c1
      sleep 5
  done

echoes the output below:
2011-04-04 10:31:03,710;ConcurrSessCnt=57
2011-04-04 11:28:46,792;ConcurrSessCnt=75
2011-04-04 11:31:13,334;ConcurrSessCnt=71
2011-04-04 11:34:44,137;ConcurrSessCnt=75
2011-04-04 11:34:44,137;ConcurrSessCnt=75
2011-04-04 11:34:44,137;ConcurrSessCnt=75

but additional lines that I appended to apend.txt
are not echoed


0
TomuniqueSr. AIX AdminCommented:
Wow..

tail -f {file}    works
appending lines to that file from another session shows up in the tail -f output
cat testfile to while read loop, echoing records .... Works...

But, combining tail -f, with the while read loop  produces no output, and hangs

I'm sorry.... I'm at a loss
 
Any chance in the world you have HP Support?  
Call it in as a bug..
0
sunhuxAuthor Commented:

Yes, sounds like a HP-UX issue.  I don't have OS/software support with HP,
only hardware.

Btw, is there any way we can recode
   tail -1 -f xxxServer.log | awk -F= '{print $2}' | while read c1
say using cat & while ?
0
sunhuxAuthor Commented:

Or perhaps replace while or for  loop?  I'm not good at this
0
sunhuxAuthor Commented:

I've recoded using for loop, I can interrupt (by Ctrl-C) the loop
but still no echo :

for c1 in `tail -1 -f ape.tx | awk -F= '{print $2}'`
do
  echo $c1
  date
done


I've tried using Bourne & Korn shells but still the same results.
C shell won't accept the syntax.  I tried on another HP-UX box
(B11.23) & got the same result.  So B11.11 or B11.23 same thing

What's your version of HP-UX?

0
ozoCommented:
#!/usr/bin/perl
use File::Tail;
my $ref=tie *FH,"File::Tail",(name=>"xxxServer.log");
$\=$/;
while( <FH> ){
        print /\s(\S+)/;
}
0
sunhuxAuthor Commented:

/tmp/rd.pl     gave:

Can't locate File/Tail.pm in @INC (@INC contains: /opt/perl/lib/5.8.2/PA-RISC1.1-thread-multi /opt/perl/lib/5.8.2 /opt/perl/lib/site_perl/5.8.2/PA-RISC1.1-thread-multi /opt/perl/lib/site_perl/5.8.2 /opt/perl/lib/site_perl .) at ./rd.pl line 2.
BEGIN failed--compilation aborted at ./rd.pl line 2.


I remember there's something missing with my HP-UX Perl library.


cat /tmp/rd.pl
#!/usr/bin/perl
use File::Tail;
my $ref=tie *FH,"File::Tail",(name=>"/tmp/apend.tx");
$\=$/;
while( <FH> ){
        print /\s(\S+)/;
}

0
sunhuxAuthor Commented:

I put a "sleep 2" in my original Shell script as tfewster suggested &
now my script consumes only about 0.10% of CPU
0
TomuniqueSr. AIX AdminCommented:
my system is 11.11

Here's another debugging option...
put a tee command between the awk and the while..

tail -1 -f ippt2activecount_ManagedIpptServer.log | awk -F= '{print $2}' |tee yourlog| while read c1
do
   echo $c1
done

THen from another window... cat yourlog  and see if it's getting data.
   (tee writes to both stdout AND  afile... so it will pass stdout to while, and create a log file for you.

----------------
Yet another option...

mkfifo  myfifo_log
tail -f ippt2activecount_ManagedIpptServer.log |awk -F= '{print $2}' > myfifo_log   &

while read c1
do
   echo $c1
done  < myfifo_log
0
sunhuxAuthor Commented:

With this script below :

tail -1 -f ippt2activecount_ManagedIpptServer.log | awk -F= '{print $2}' |tee yourlog| while read c1
do
   echo $c1
done

"cat 1_line.txt >> yourlog" to append 1 line into the log will result in
yourlog being overwritten with that 1 line & nothing was output to
screen by the codes above
0
TomuniqueSr. AIX AdminCommented:
No, don't cat INTO yourlog   TEE is writing into yourlog


the tee command writes two strams    a file stream, and stdout stream
I use it in the example above to create a "debug log" (named yourlog).  Just to see what's being fed into the while loop, as earlier in the thread we'd found that tail | awk  was in fact producing output, but when piped into while-read  it did not.

So...

your "cat 1_line" needs to append to whatever the tail command is monitoring.
(if it's a production log, then make  a copy to play  with)..

WHen you cat to the log tali is monitoring... it should be pickedup by tail, passed to tee, and written to "yourlog"
Then "cat yourlog" and see if your 1_line showed up..

It should.. and if it did, tail's working... and we're back to a broken while-read.

0
simon3270Commented:
It's a buffering problem.
tail -f input_file | while read c1
do
  echo $c1
done

Open in new window

works as expected (as each line is added to the input_file, it is echoed).

If you add awk with:
tail -f input_file | awk -F= '{print $3}' | while read c1
do
  echo $c1
done

Open in new window

then the output from "tail" is buffered in blocks (4096 bytes on Linux, 8192 on HPUX, in my testing), so until you add a block's worth of data, tail doesn't output anything.

One fix, if you have to use the awk, might be to nest while loops:
tail -f input_file | while read lin
do
  echo "$lin" | awk -F= '{print $3}' | while read c1
  do
    echo $c1
  done
done

Open in new window


By the way, the Ctrl-C failure is possibly because the loop won't break until it is trying to process output - when I had my 8192-byte blocks on HPUX, I had to add lots of data after pressing Ctrl-C to get it to stop.
0
tfewsterCommented:
Excellent analysis of the issue, simon3270 - I'd forgotten the odd pitfalls of buffering and couldn't see anywhere it would be occurring.

One minor query - why have a second "while" loop? Once raw input is received from the `tail` command, just format it and then process it...

tail -f input_file | while read lin
do
  # format the input
  c1=`echo "$lin" | awk -F= '{print $3}'`
  echo $c1
 
  # Original code to process results

  c2=c1

done
0
sunhuxAuthor Commented:
excellent
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Shell Scripting

From novice to tech pro — start learning today.