Re: Bash: command output to variable
- From: Steffen Schuler <schuler.steffenDELETETHIS@xxxxxx>
- Date: Wed, 10 May 2006 09:19:43 +0200
John W. Krahn wrote:
Steffen Schuler wrote:
Dive wrote:
I have a small script which works like a mini top command. It uses ps to
list top 10 cpu intensive processes. This works ok but what I now want
to do is log when a process hits 10 or more cpu%.
Something like this:
#! /bin/bash
rm toplog
while true
do
clear
ps -eao pcpu,pmem,comm --no-headers --sort=-pcpu | head
a=`ps -eao pcpu,pmem,comm --no-headers --sort=-pcpu | head |
cut -f 2 -d ' '`
if $a >= 10.0 # <-- this is the problem
then
echo -n `date +%T` " ">> toplog
ps -eao pcpu,pmem,comm --no-headers --sort=-pcpu |
head -n 1 >> toplog
fi
sleep 5
done
# EOF
Sample output:
1.5 0.8 slrn
0.8 1.0 gkrellm
0.5 4.2 firefox-bin
0.5 5.1 X
0.1 0.6 vim
0.0 0.3 mrxvt
0.0 0.7 mrxvt
0.0 2.1 gaim
0.0 0.6 adesklets
0.0 0.8 fluxbox
[snip]
An optimized perl solution:
Optimized?
--------------- begin(top.pl) -------------------------
#!/usr/bin/env perl
You are calling the env program to run perl. Why not just call perl directly?
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;
use English;
perldoc English
[snip]
PERFORMANCE
This module can provoke sizeable inefficiencies for regular
expressions, due to unfortunate implementation details. If performance
matters in your application and you don't need $PREMATCH, $MATCH, or
$POSTMATCH, try doing
use English qw( -no_match_vars ) ;
. It is especially important to do this in modules to avoid penalizing
all applications which use them.
our $BSD_STYLE = 0; # TODO: should be set to the correct value
my $logfile = "toplog";
$English::OUTPUT_AUTOFLUSH = 1;
my $clear_string = `clear`;
open(LOG, ">$logfile") or
die "can't open \"$logfile\": $!";
while (1) {
my $first_line = "\n";
my $with_great_value = 0;
print $clear_string;
if (open(PSOUT,
"ps -eao pcpu,pmem,comm --no-headers --sort=-pcpu |")) {
for (my $i = 0; $i < 10; ++$i) {
if (defined($_ = <PSOUT>)) {
print;
if ($i == 0) {
$first_line = $_;
s/^\s*//;
my @F = split /\s+/;
s/^\s*// modifies every string whether it begins with whitespace or not. For
efficiency just use split with no arguments:
my @F = split;
But since you are only using $F[0] you can simply do:
my ( $F ) = split;
Which is more efficient because it stops splitting after it fills the
variable(s) in the list.
$with_great_value = 1 if ($F[0] >= 10.0);
}
}
if ($with_great_value) {
my @T = localtime(time);
printf LOG "%02d:%02d:%02d %s", @T[2,1,0], $first_line;
The POSIX::strftime function is usually more efficient:
use POSIX 'strftime';
print strftime( '%T', localtime ), " $first_line";
Even if you don't use that you can avoid creating the @T array:
printf LOG '%02d:%02d:%02d %s', ( localtime )[ 2, 1, 0 ], $first_line;
}
}
close(PSOUT);
You should also verify that the piped open filehandle was closed correctly:
perldoc -f close
}
my $key = getKeyQuiet(5);
The Perl FAQ has some code that do that without running an external program:
perldoc -q "How can I read a single character"
if ($key =~ /q/i) {
exit 0;
}
}
# reads a key without echo and with a timeout of $timeout seconds
sub getKeyQuiet
{
[snip]
John
Thank you very much John for your corrections of my code. You have done a superb job. The corrected code follows:
------------------------- (top.pl) ---------------------------------
#!/usr/bin/env perl
# more optimal but less portable as first line:
#!/usr/bin/perl
# to quit the command you have to enter 'q' or 'Q'
# some pragmas to help error finding
use strict;
use warnings;
use diagnostics;
# perlvar: '-no_match_vars' is recommended for performance reasons
use English '-no_match_vars';
use POSIX 'strftime';
# should be installed from CPAN
use Term::ReadKey;
my $logfile = "toplog";
$English::OUTPUT_AUTOFLUSH = 1;
my $clear_string = `clear`;
open(TTY, "</dev/tty");
ReadMode "normal";
open(LOG, ">$logfile") or
die "can't open \"$logfile\": $!";
while (1) {
my $first_line = "\n";
print $clear_string;
if (open(PSOUT,
"ps -eao pcpu,pmem,comm --no-headers --sort=-pcpu |")) {
for (my $i = 0; $i < 10; ++$i) {
if (defined($_ = <PSOUT>)) {
print;
my ($F) = split;
print LOG strftime( '%T', localtime ), " $_" if $F >= 10.0;
}
}
} else {
die "can't open pipe from command: $!";
}
my $key = readKeyQuit(5);
if (defined($key) and $key =~ /q/i) {
exit 0;
}
}
close(PSOUT) or die "cant't close piped command: $!";
close(LOG) or die "can't close \"$logfile\": $!";
close(TTY) or die "can't close \"/dev/tty\": $!";
# reads key with a timeout of 5 seconds without echo
sub readKeyQuit
{
my $timeout = shift;
my $key = '';
eval {
# start alarm handler after $timeout seconds
local $SIG{ALRM} = sub { die "alarm clock restart" };
alarm $timeout;
ReadMode "raw";
# ReadKey 5, *TTY; doesn't seem to work
$key = ReadKey 0, *TTY;
ReadMode "normal";
# start alarm handler immediately
alarm 0;
};
# if the Perl syntax error message string exists and is not
# "alarm clock restart" then die
if ($EVAL_ERROR and $EVAL_ERROR !~ /alarm clock restart/) { die };
return $key;
}
---------------------- end(top.pl) -------------------------------
Greetings from Munich,
Steffen
.
- Follow-Ups:
- Re: Bash: command output to variable
- From: Steffen Schuler
- Re: Bash: command output to variable
- References:
- Bash: command output to variable
- From: Dive
- Re: Bash: command output to variable
- From: Steffen Schuler
- Re: Bash: command output to variable
- From: John W. Krahn
- Bash: command output to variable
- Prev by Date: WANdisco Announces New High Availability Disaster Recovery Solution for CVS, Subversion and CVSNT.
- Next by Date: Re: () $() etc., in Bash
- Previous by thread: Re: Bash: command output to variable
- Next by thread: Re: Bash: command output to variable
- Index(es):
Relevant Pages
|