Grabbing value from command output and monitoring for changes

Hi all,

Very new to shell scripting so appreciate some help!

There is a process count that I need to monitor, I have the AIX command that gives this value and I've cleaned it up with grep/awk so it only spits out the value I'm interested in:

echo "psc -i 10050 -s RELOAD_SERVICE" | tmadmin | grep -vE 'Name|------' | awk '{print $7}'

I need to create something to monitor this value (numerical) at given time intervals and then write to a log file when it changes, along with a timestamp.

I got something that'll put this into a file:

#!/bin/sh
echo "psc -i 10050 -s RELOAD_SERVICE" | tmadmin | grep -vE 'Name|------' | awk '{print $7}' | while read newvalue;
do
echo $newvalue>>test.log
done

..but now I'm stuck reading that file and comparing it to the new value. Or perhaps there's a way to continuously monitor the value and do things more efficiently?

Any help or suggestions would be much appreciated!!
Thanks,
Monty

Try something like this:


last_value=x
log_file=/tmp/log.file
while true
do
   your-command-string | read new_value
   if [[ $last_value != $new_value ]]
   then
       echo "$(date) $new_value" >>$log_file
       last_value=$new_value
   fi
   sleep 300      # every 5 minutes
done

This assumes bash -- most modern UNIXes link /usr/bin/sh to bash -- the [[ expression on the if are much more efficient. If AIX doesn't link to bash, and/or ksh isn't available, you'll have to use if [...].

1 Like

Thanks!! Looking good, however I think there's a problem with my command as it seems to be pulling blanks instead of the value I want. I put an echo $new_value in there to prove this and sure enough it's blank.

How do I use grep -vE (or something else if more appropriate) to strip and blanks from my command output and just catch the value itself?

Thanks!
Monty

I assumed that your command ran a single time, though looking back at your example maybe it executes continuously.

Small changes:

last_value=x
log_file=/tmp/log.file

your-command-string | while read new_value
do
   if [[ $last_value != $new_value ]]
   then
       echo "$(date) $new_value" >>$log_file
       last_value=$new_value
   fi
done

The only catch to this is that your command needs to block otherwise you'll be executing a pretty tight loop and possibly taxing resources. My first example is preferred as it puts you in control of how often you execute the command that checks on the system state.

---------- Post updated at 21:22 ---------- Previous update was at 21:14 ----------

I don't think it's the grep command. Can you post the output of the first few lines of just this part of your command:

echo "psc -i 10050 -s RELOAD_SERVICE" | tmadmin | grep -vE 'Name|------'

I suspect that maybe the line is bar separated and not space/tab separated and thus printing $7 might be producing null output because there is no field 7. If the output is bar separated, then you can change your awk command to supply -F "|" to specify the field separator.

1 Like

My command runs a single time, looks like this:

tmadmin - Copyright (c) 1996-1999 BEA Systems, Inc.
Portions * Copyright 1986-1997 RSA Data Security, Inc.
All Rights Reserved.
Distributed under license by BEA Systems, Inc.
Tuxedo is a registered trademark.
TMADMIN_CAT:199: WARN: Cannot become administrator.Limited set of commands available.

3

...I think there's a blank on top of the 3 that needs stripping somehow.

If I put an echo in your code:

#!/bin/sh
last_value=x
log_file=/tmp/log.file
while true
do
   echo "psc -i 10050 -s RELOAD_SERVICE" | tmadmin | grep -vE 'Name|------' | awk '{print $7}' | read new_value
   echo $new_value
   if [[ $last_value != $new_value ]]
   then
       echo "$(date) $new_value" >>$log_file
       last_value=$new_value
   fi
   sleep 300      # every 5 minutes
done

..it doesn't show the new_value it should have read from my command.

Thanks!
Monty

Good -- a single shot command is much easier to deal with.

I miss read the grep -- realise now that you are ditching the 'name' line. If the value that you are interested in is always the last line, which it appears to be, then this will be better:

echo "psc -i 10050 -s RELOAD_SERVICE" | tmadmin | grep -vE 'Name|------' | tail -1| read new_value;

If there is more output following the value you want, but it is always line seven, then try this:

echo "psc -i 10050 -s RELOAD_SERVICE" | tmadmin | grep -vE 'Name|------' | awk 'NR==7 {print}' | read new_value;

Have a go with one of these and see if that works.

1 Like

Thanks again, either one of those might work but I went with this in the end:

echo "psc -i 10050 -s RELOAD_SERVICE" | tmadmin | grep -vE 'Name|------|'^$'' | awk '{print $7}'

..only thing that's not perfect now is that it (understandably) will always write a row to the log when you first fire it up. Any way to avoid this?

Thanks a million!!
Monty

Cool -- glad you found something that works for you. Yes, change the if a bit to skip if last_value is 'x' or whatever you initialised it to. Something like:

 if [[ $last_value != "x"  &&   $last_value != $new_value ]]
   then
       echo "$(date) $new_value" >>$log_file
   fi
last_value=$new_value

You will also always have to capture the current value in last_value, so move that line following the fi. There are other ways, but I think this is pretty straight forward.

Cheers!

1 Like

Can't seem to get that last change to work, I get this:

./adam.sh[8]: [[x: not found
./adam.sh[8]: x: not found

Any ideas?
Thanks!
Monty

Oops, typo.

The [[ needs to have a trailing space.

if [[ $last_value != "x" ||  $last_value != $new_value ]]

Sorry bout that.

1 Like

Those suggestions will often fail, since some very popular shells (bash included) will run the read command at the end of the pipeline in a subshell. The value assigned to new_value will never be visible in the script's environment, since it will only ever exist in the subshell's environment (which is destroyed as soon as the read is completed).

Example using bash:

$ x=; echo hello | read x; echo $x

$ x=; echo hello | ( read x; echo SUBSHELL: $x ); echo CURRENT: $x
SUBSHELL: hello
CURRENT:

Regards,
Alister

Right about bash, however Ksh does the right thing and this will work in ksh.

$ cat t19
#!/usr/bin/env ksh
x= 
echo hello | read x 
echo $x

$ t19
hello
1 Like

Thanks for the correction .. still doesn't work however, it just writes to the log on startup and after the sleep. Confused, looks like it should work to me.

Cheers,
Monty

Bugger! I need to call it a night and go to bed.

Test should be AND rather than OR.

if [[ $last_value != "x"  &&  $last_value != $new_value ]]
1 Like

Sir, you are a legend! Works a charm.

Thanks!!
Monty