quick and dirty shell vs elegant perl

P

Phil Meyer

Part of the objective is to find a reliable way to discover the qlogic fiber
channel card WWN and which WWN(s) it connects to. This data is then stored
and compared to WWN data from the SAN fabric to determine which switch port
its connected to, and also which storage devices it is talking to.

Assumptions: Solaris systems, and explorer output as only source.

All of that aside, this is indeed a generic type issue, ie: is it ever
'better' to jump out to the shell for a quick and dirty, vs learning the
'better way' in perl. Honestly, this may be embarrassing to me, but it
is troublesome to look at a problem and have my mind see hundreds of lines
of perl code, or a few simple lines of shell commands.

OK here is the detail: The filenames are consistant and start with
luxadm_display_
There can be over 34,000 lines returned.

The function here is pretty self contained, so that this should be a
pretty clear example. And this style of C programming was fashionable
before most of you were born, so please try to limit the comments about
where curlies go. Thanks!

#!/usr/bin/perl -w

$ENV{'ORACLE_HOME'} = "/oracle/product/8.1.7a";

use File::stat qw:)FIELDS);
use DBI;
use strict;


get_WWN_sun()
{
my $cmd = "";
my $g = "";
my @data = "";
my $lun = "";
my $host_port = "";
my $i = 0 ;
my $line = "";
my $loc = "/export/data/explorer/blah_blah_blah/disks/luxadm_display*";

# This is the code in question -- How do do this as well in perl
# grep each half into seperate files, then paste them together.

$cmd = "grep -h \"LUN path port WWN\" $loc > /tmp/LUNs";
@data = system($cmd);
$cmd = "grep -h \"Host controller port WWN\" $loc > /tmp/Hosts";
@data = system($cmd);
$cmd = "paste /tmp/LUNs /tmp/Hosts | sort -u";
@data = `$cmd`;

# At this point I have boilded thousands of lines of data to just a couple.
# what is the better way?
# From here down is just database stuff, and yes it would be better to
# bind the variables, but the data is known and there are never special chars
# db connections are global

for ( @data )
{
($g,$g,$g,$g,$lun,$g,$g,$g,$g,$host_port) = split();
$line = "SELECT count(*) from WWN where
system like \'$host\' and
lun like \'$lun\'";
$i = $dbh->selectrow_array($line);
if ( $i == 0 )
{
$line = "INSERT INTO WWN VALUES
( \'$host\',\'$lun\',\'$host_port\',\'\',\'$drvr\',\'\' )";
print $line;
$sth0 = $dbh->prepare($line);
$sth0->execute();
}
else
{
$line = "UPDATE WWN SET
host_port = \'$host_port\',
driver = \'$drvr\'
where
system = \'$host\' and
lun = \'$lun\'";
print $line;
$sth0 = $dbh->prepare($line);
$sth0->execute();
}
}
}
 
T

Tassilo v. Parseval

Also sprach Phil Meyer:
Part of the objective is to find a reliable way to discover the qlogic fiber
channel card WWN and which WWN(s) it connects to. This data is then stored
and compared to WWN data from the SAN fabric to determine which switch port
its connected to, and also which storage devices it is talking to.

Assumptions: Solaris systems, and explorer output as only source.

All of that aside, this is indeed a generic type issue, ie: is it ever
'better' to jump out to the shell for a quick and dirty, vs learning the
'better way' in perl. Honestly, this may be embarrassing to me, but it
is troublesome to look at a problem and have my mind see hundreds of lines
of perl code, or a few simple lines of shell commands.

It really depends. Your script is a typical system-admin's script.
Things like portability between platforms, beauty etc. aren't a concern
in these cases. I also have a couple of such scripts: No beauty-award
for them, but they get their job done.

If you find that a problem can be solved more quickly using external
tools, then do so.
OK here is the detail: The filenames are consistant and start with
luxadm_display_
There can be over 34,000 lines returned.

The function here is pretty self contained, so that this should be a
pretty clear example. And this style of C programming was fashionable
before most of you were born, so please try to limit the comments about
where curlies go. Thanks!

:) Ok.
#!/usr/bin/perl -w

$ENV{'ORACLE_HOME'} = "/oracle/product/8.1.7a";

use File::stat qw:)FIELDS);
use DBI;
use strict;


get_WWN_sun()
{
my $cmd = "";
my $g = "";
my @data = "";
my $lun = "";
my $host_port = "";
my $i = 0 ;
my $line = "";
my $loc = "/export/data/explorer/blah_blah_blah/disks/luxadm_display*";

# This is the code in question -- How do do this as well in perl
# grep each half into seperate files, then paste them together.

$cmd = "grep -h \"LUN path port WWN\" $loc > /tmp/LUNs";
@data = system($cmd);
$cmd = "grep -h \"Host controller port WWN\" $loc > /tmp/Hosts";
@data = system($cmd);
$cmd = "paste /tmp/LUNs /tmp/Hosts | sort -u";
@data = `$cmd`;

The above code suggests that system(COMMAND) actually returns the output
of a command. It doesn't. It returns a success- (or failure-) code. See
'perldoc -f system'. Backticks can be used to capture the output (as you
demonstrated in the last line of the above).

Doing the above in Perl could look like this [untested]:

my %lines; # used to make lines unique
local @ARGV = </export/data/explorer/blah_blah_blah/disks/luxadm_display*>;
local $_; # just in case
while (<>) {
chomp;
$lines{ $_ } if index($_, "Host controller port WWN") != -1 or
index($_, "LUN path port WWN" != -1;
}
my @data = sort keys %seen;

This already uses a few short-cuts, like employing glob() in order to
avoid a tedious opendir/readdir/grep combination to select the right
files. Then it assigns them to @ARGV so that we don't have to open them
and can just iterate over the special empty file-handle (<>).

Filling the matching lines into a hash ensures that they are unique. I
used index because it may be slightly quicker than a fullblown regex.
Your grep pattern is just a string so index() works fine for that.
Finally we just sort the keys and assign them to @data.
# At this point I have boilded thousands of lines of data to just a couple.
# what is the better way?
# From here down is just database stuff, and yes it would be better to
# bind the variables, but the data is known and there are never special chars
# db connections are global

for ( @data )
{
($g,$g,$g,$g,$lun,$g,$g,$g,$g,$host_port) = split();

You only want two values, so use a list-slice here:

my ($lun, $host_port) = (split)[4,9];
$line = "SELECT count(*) from WWN where
system like \'$host\' and
lun like \'$lun\'";
$i = $dbh->selectrow_array($line);

Here you should better be using DBI's quoting features:

my $sth = $dbh->prepare(<<EOS);
SELECT count(*) from WWN where system like ? and lun like ?
EOS
$sth->execute($host, $lun);
# now use $sth->fetchrow_array or so to retrieve the results

The advantage of this is that the quoting will be done for you. But
depending on the values that can be in $host and $lun there might not be
any quoting needed.

[...]

Tassilo
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top