Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Locked thread
Kidane
Dec 15, 2004

DANGER TO MANIFOLD
i'm using getopt::long to get command line arguments for my script. my problem is this, I want the script to die (or at least show my usage message) if someone passes a bad argument to the script. however, when someone passes a good argument then a bad argument the script executes, albeit with the message "Unknown option: foo".

is there a way to catch this kind of exception?

GOOD:
code:
[root@nickforsale ~]# ./fixstats.pl -aasd
Unknown option: aasd

Usage: ./fixstats.pl [--domain=<domain> | --all | --verbose | --help]

--domain        : Run statistics on specified domain only. May not be used in conjunction with --all.
--all           : Run statistics on all domains. May not be used in conjunction with --domain.
--verbose       : Verbose mode. Displays domains as they are processed.
--help          : This message.
BAD:
code:
[root@nickforsale ~]# ./fixstats.pl --domain=blah.com -q
Unknown option: q
got domain: -blah.com-
the main program loop starts after my 'got domain' debug thingie, and i would rather people are passed to the usage script when they enter a bad argument no matter what. i will paste my terrible code now:

code:
GetOptions(\%options, "domain:s", "all", "help", "verbose");

if ($options{verbose}){
       $verbose = 1;
}
if ($options{help}) {
       show_syntax();
}
if ($options{all}) {
       $alldomains = 1;
}
if ($options{domain}) {
       if ($options{all}) {
               show_syntax();
       }
       if (length($options{domain}) == 0) {
               show_syntax();
       }
       print "got domain: -$options{domain}-\n";
       $alldomains = 0;
       @vhosts = ($options{domain})
}
if (not %options) {
       show_syntax();
}

Adbot
ADBOT LOVES YOU

Kidane
Dec 15, 2004

DANGER TO MANIFOLD
Yes! Awesome, I've got a beginner's perl book which devotes one paragraph to getopt::long and doesn't mention the configure option. So basically anything extra will cause ARGV to have an option set, yes yes yes, thank you v. much.

Kidane
Dec 15, 2004

DANGER TO MANIFOLD
Essentially, I'm trying to match one array against another. Here is what I've got:

code:
foreach(@php_available_modules) {
    @already_installed = grep $_, @php_installed_modules;
    @available_to_install = grep(/[^$_/], @php_installed_modules);
}
The first grep works perfectly, however I don't know how to say "grep for $_ in @php_installed_modules and if you DON'T find it, add $_ to some array". In fact given the syntax of grep in perl I'm not sure it's possible... I would write a regular expression which interpolates $_ but I don't think I'm allowed to do that either. :confused:

Intermediate Perl states:

"While the grep is running, it shadows any existing value in $_, which is to say that grep borrows the use of this variable but puts the original value back when it's done."

My interpretation is that I needn't worry about copying $_ into an intermediate variable, but I will probably try that just in case.

Kidane fucked around with this message at 12:10 on Mar 7, 2008

Kidane
Dec 15, 2004

DANGER TO MANIFOLD

magimix posted:

I found your description of the problem a bit confusing to be honest. So, assuming I haven't entirely misunderstood you, might the following do what you want?

my @installed;
my @available;
my %installed = map {$_ => 1} @php_installed_modules;
foreach my $module (@php_available_modules)
{
exists $installed{$module} ?
push @installed, $module :
push @available, $module;
}

So @installed contains those php_available_modules that are in php_installed_modules, and @available contains those php_available_modules that are not in php_installed_modules.
Yes, that's exactly what I'm trying to do.

I considered using a hash but the sub started swelling up and I'd like to keep this portion of my code to as few lines as possible.

Kidane
Dec 15, 2004

DANGER TO MANIFOLD

magimix posted:

I'd say worry about the functional aspect of your code for now, rather than its line-count. I created a hash of php_installed_modules so that I could iterate through php_available_modules once only, and do a single hash-lookup for each element therein.

Your original code has you iterating though php_installed_modules twice for every element in php_available_modules.
You're right, of course. I've never used map before so I was shying away from it. My original code (before I tried using grep) looked like this
code:
foreach (@php_available_modules) {
    my $avail = $_;
    $installed = 0;
        foreach(@php_installed_modules) {
            if ($avail eq $_) {
                $installed = 1;
            }
        }
        if (not $installed) {
            push(@available_to_install, $avail);
        }
        else {
            push(@already_installed, $avail);
        }
}
Which is very awkward (I've only been doing this for a couple months). Your code worked perfectly so I am going to look in to what map does and add it to my toolbox.

Thanks a lot for your quick response! :D

Kidane
Dec 15, 2004

DANGER TO MANIFOLD

magimix posted:

I'm still got a nagging doubt that I'm missing something. Anyway, would the following also do what you want?

code:
my %installed = map {$_ => 1} @php_installed_modules;
my @available = grep {!exists $installed{$_}} @php_available_modules;
In my test code, this produces the same @available as my previous code does. This is predicated around the fact that in the previous code @installed always had a one to one correlation with @php_installed_modules, so there was no sense duplicating that data. It also depends on the set of installed modules always being a subset of the available modules.
Ah, ok. This is part of the data I'm working with:

PHP modules already installed (from php -m):

code:
bcmath
bz2
calendar
ctype
curl
date
dbase
dom
exif
filter
ftp
gd
gettext
gmp
hash
iconv
imap
ionCube Loader
json
ldap
And these are the modules offered in the PHP source code:

code:
bcmath
calendar
dotnet
ctype
curl
date
dba
dbase
dom
exif
fbsql
fdf
filter
ftp
gd
gettext
gmp
hash
iconv
imap
interbase
json
ldap
I'm setting up a script to automate, for our users, a way to easily install PHP modules which we did not include on their server but can be easily compiled as module from the existing source. So I want to make sure they can only install modules which are already contained in the source, but aren't already installed. Pretty basic stuff.

Kidane fucked around with this message at 12:55 on Mar 7, 2008

Kidane
Dec 15, 2004

DANGER TO MANIFOLD
Stupid question...

Is there a way to print to stdout without updating the cursor? For example if I wanted to display a percentage done indicator, or even just a spinning | to show that the program is thinking, is there a way to do this? My intuition tells me that it's not but I figured I might as well ask (Google was no help).

Kidane
Dec 15, 2004

DANGER TO MANIFOLD

dagard posted:

This looks like what you want:
http://outflux.net/software/shorts/perl/Idle
Haha, of course, printing a carriage return will move the cursor back to the beginning of the line, why didn't I think of that? :doh:

Thanks!

Kidane
Dec 15, 2004

DANGER TO MANIFOLD

Sartak posted:

Please call it Perl or perl. PERL reeks with everything wrong about the language.


-s $file returning the empty string means it couldn't find the file. Are you sure you're in the right directory and that $file is there?

If you're building up @files through readdir, they won't have the directory name prepended - you have to manually do that.
Also, why not use a hash to store the filename and size, rather than hacking the size onto the end of the name? Hashes are perfect for stuff like this. Then you could just do:

code:
foreach (keys %files) {

    my $file_name = $_;
    my $file_size = $files{$_};
    print "$file_name ($file_size)\n";

}

Kidane fucked around with this message at 00:18 on Mar 23, 2008

Kidane
Dec 15, 2004

DANGER TO MANIFOLD
I had the same problem with Net::ssh::Perl and ended up using Expect.pm to interface with SSH instead.

Kidane
Dec 15, 2004

DANGER TO MANIFOLD

Triple Tech posted:

1. It's an object* that you're trying to look at in scalar context, i.e. you're not interfacing with it as intended. * There are no real objects in Perl, just a system of blessed hashes.

2. Because the slice notation works fine for lists. You could say print +(1, 2, 3)[0] and it prints 1 (the plus sign is unrelated but required for this example).

3. Not even sure what you mean.

4. Probably, but you shouldn't, for the sake of clarity.

Regarding question #1 you may want to try dumping the object since it may help you visualize it better.

Use Data::Dumper;
<code...>
$response = $resolver->send($domain);
print Dumper $response;

Kidane
Dec 15, 2004

DANGER TO MANIFOLD
I also like Getopt::Euclid, since it basically takes your documentation and generates command line arguments out of it. For this poster's needs though, I would probably parse @ARGV myself.

Kidane
Dec 15, 2004

DANGER TO MANIFOLD

Shoes posted:

I'm guaranteed that this is going to be a list of a few hundred items. Is there a way to call 10 or 20 at a time?

I would suggest Parallel::ForkManager. I've used it at my job a few times because it's dead simple and Just Works.

Kidane
Dec 15, 2004

DANGER TO MANIFOLD
If I may be so bold: http://search.cpan.org/~bingos/Archive-Tar-1.54/lib/Archive/Tar.pm

For quick and dirty stuff system() and `` are fine, for anything I actually need to maintain I will find a module that does what I need (or write one). The one exception of course is ssh, I haven't been able to successfully install any pure-perl version of ssh on any system ever.

Kidane
Dec 15, 2004

DANGER TO MANIFOLD

Paradox posted:

I'm curious about what problems you've encountered when trying to install Net::ssh::Perl. Usually installing perl modules is quite easy. What were the unique troubles with this one?
It's a very complex pure perl implementation of SSH, it requires a ton of nonstandard math and encryption libraries which need to be manually downloaded from some website, yada yada. Apparently there's a debian package for it so that might actually work. Unfortunately when I had cause to use that module I was using a Redhat system and was unable to find a package for it via yum (there may be one now, I haven't dealt with this in over a year).

I ended up using Expect.pm, which worked great, but anything I ever write using Expect feels like a hack.

Kidane
Dec 15, 2004

DANGER TO MANIFOLD

syphon posted:

Ok, so how do I loop over each array element and access that data? Running "print $array->[0]->{CLOCKSPEED};" does indeed output '2400 MHz', but running
code:
for (@{$Processors}) 
  { 
    print $Processors->[$_]->{CLOCKSPEED}; 
  }
outputs an infinite loop of "2400 MHz", even though the array only has 2 elements.

code:
foreach my $cpu (@{$processors}) {
    foreach my $cpu_param (keys %$cpu) {
        print $cpu_param, ': ', $cpu->{$cpu_param}, "\n";
    }
    print "\n";
}
Not checked for syntax at all, sorry.

Edit: didn't realize how many replies there were, my bad. :doh:

Adbot
ADBOT LOVES YOU

Kidane
Dec 15, 2004

DANGER TO MANIFOLD

Canine Blues Arooo posted:

That right there is a thing of beauty!
You should probably step back and rethink what you are doing or the way you are storing data. I can't think of a compelling reason to reference hash data by index. If you really need to, you can have an array full of hash references but again I'm not sure why you would need that. Maybe you could elaborate on what you're trying to accomplish?

  • Locked thread