kehansen has asked for the wisdom of the Perl Monks concerning the following question:

I was asked to analyze a perl script by my manager. I am a novice at PERL. The script contains some "use" statements, some variable settings, and two sub routines. But there is no call to these subroutines. The script does run when executed. How can it run without explicitly calling the subroutines?

Replies are listed 'Best First'.
Re: Scripts with only sub-routines?
by Burak (Chaplain) on Mar 24, 2008 at 20:17 UTC
    run it in the debugger to see how and what it calls:
    perl -d script.pl
    See debugger.perl.org for more informartion on the debugger.
Re: Scripts with only sub-routines?
by kyle (Abbot) on Mar 24, 2008 at 20:29 UTC

    Some details would help here. Without having a look at what you have, I can only guess.

    Modules that you use can execute code.

    A sub BEGIN is actually a block that runs without being called, if you have any of those. See perlmod for more info and other blocks that might act this way.

    You speak of some variable settings. Something like "my $host = `hostname` is actually executing the hostname shell command (note the backticks, not single quotes).

      Modules that you use can execute code

      Exactly; and the code they execute may not be immediately obvious.

      #!/usr/bin/perl use strict; use warnings; use EndRunner qw( foo ); sub foo_one { print "Foo one called!\n"; } sub foo_two { print "Foo two called\n"; } sub foo_dies { die "Aieee!\n"; } our $foo_bad_example = "no problem here"; __END__ ## This would go in EndRunner.pm package EndRunner; use strict; use warnings; our @ISA = qw( Exporter ); require Exporter; my @packages_to_run; sub import { my( $package, @args ) = @_; my $caller_package = ( caller( 1 ) )[0]; for my $prefix ( @args ) { push @packages_to_run, [ $caller_package, $prefix ]; } return; } END { no strict 'refs'; for my $items ( @packages_to_run ) { my( $package, $prefix ) = @{ $items }; for my $sub ( grep /^${prefix}_/, keys %{ $package . "::" } ) { my $sub_ref = *{"${package}::$sub"}{CODE}; next unless defined $sub_ref; eval { $sub_ref->(); }; if( $@ ) { warn "EndRunner: problem calling '$sub' in $package: $@\n"; } } } } 1; __END__

      Just imagine what chaos one could foment if you changed that to (say) rot13 the prefix before picking what to run, or generated the prefix based on the phase of the moon . . . :)

      The cake is a lie.
      The cake is a lie.
      The cake is a lie.

Re: Scripts with only sub-routines?
by moritz (Cardinal) on Mar 24, 2008 at 20:31 UTC
    There are a number of ways that a program can actually do something without any obvious subroutine calls.

    use Module; loads that module, and calls the sub import in that module. You can do things there. (This happens at compile time.

    When you have a sub called BEGIN, INIT, CHECK or END it is automatically invoced, either at compile time, after the program has terminated (or more obscure times. See perlsub for more details).

    If you're new to perl (which is not spelled PERL btw.) it might be that you don't recognize a sub call even though there is one. In many cases just the name of the sub without any parenthesis is enough to call it.

      Thank you. I will look in the modules called by the 'use' statements and review them further. BTW. This is my first time using this site. It looks like it will be very helpful. In my job I have to support & create scripts in sh, bash, ksh, csh, expect, and perl so I don't get dedicated time in a single language. In my perl coding I have not used modules very much. I did just post the code in this thread.
Re: Scripts with only sub-routines?
by apl (Monsignor) on Mar 24, 2008 at 20:26 UTC
    Could you post the code (between <code> and </code>)?
      Here is the code I am asking about.
      #! /usr/bin/perl -w use Net::SNMP; use RRDs; use strict; use vars; my @host = qw/coccp3 coccp2/; my $link = '/mrtg/work/'; my $html = '/mrtg/work/dev/hari/rrd/cpu/'; my $path = '/mrtg/work/dev/hari/rrd/cpu/'; my $debug = '0'; my $once = '1'; sub getsnmp { ### init my $s = shift; my $oid = shift || return "U"; my $response = $s->get_request($oid); my $retval = $response->{$oid} || "U"; ### debug print "$oid -> $retval\n" if $debug; ### the end return ($retval =~ /(\d+)/) ? $retval : 'U'; } sub graph { ### init my $host = shift; my $file = shift; my $span = shift; my $name = shift; my @data; ### fix data push(@data, "$html/$host-$name.png", "--start=-$span", "--vertical-label=cpu usage %", # "--width=800", # "--height=600", "--lazy", "--imgformat=PNG", "--title=cpu usage for $host", "DEF:user=$file:user:AVERAGE", "DEF:system=$file:system:AVERAGE", "DEF:idle=$file:idle:AVERAGE", "DEF:kernel=$file:kernel:AVERAGE", "DEF:interrupt=$file:interrupt:AVERAGE", "AREA:user#FF0000:user", "STACK:system#00FF00:system", "STACK:idle#0000FF:idle", "STACK:kernel#00CCFF:kernel", "STACK:interrupt#FFFF00:interrupt", ); ### rrdtool RRDs::graph(@data); if(my $ERROR = RRDs::error) { print "ERROR: $ERROR\n"; } } if($once){ ### open file open(INDEX, '>' ,"$html/index.html") or die $!; ### print html print INDEX join("\n", "<html>", "<head>", "<title>Veraz NGN Services (cpu stats)</title>", "</head>", "<body>" ); } for my $host (@host){ ### init my $file = "$path/$host-cpu.rrd"; ### debug print "$file\n" if $debug; ### create file unless(-e $file) { RRDs::create($file, "--step",300, "DS:user:COUNTER:600:0:100", "DS:nice:COUNTER:600:0:100", "DS:system:COUNTER:600:0:100", "DS:idle:COUNTER:600:0:100", "DS:kernel:COUNTER:600:0:100", "DS:interrupt:COUNTER:600:0:100", "RRA:AVERAGE:0.5:1:600", "RRA:AVERAGE:0.5:6:700", "RRA:AVERAGE:0.5:24:775", "RRA:AVERAGE:0.5:288:2000", "RRA:MAX:0.5:1:600", "RRA:MAX:0.5:6:700", "RRA:MAX:0.5:24:775", "RRA:MAX:0.5:288:2000", "RRA:MIN:0.5:1:600", "RRA:MIN:0.5:6:700", "RRA:MIN:0.5:24:775", "RRA:MIN:0.5:288:2000" ); ### error? if(my $ERROR = RRDs::error) { print "ERROR: $ERROR\n"; } } ### debug print "$host\n" if $debug; ### snmp my($s, $err) = Net::SNMP->session( -hostname => $host, -community => "public", -timeout => 1, -version => 2 ); if($err) { die "Host ($host) down: $err\n"; } ### html if($once) { print INDEX join("\n", qq(<a href="${host}.html"><img src="$link$host-day.png" border +="0"></a>), ); } $s->translate([-timeticks => 0x0]); my $uptime = getsnmp($s, ".1.3.6.1.2.1.1.3.0",1); if($uptime ne "U" && $uptime < 60000) { RRDs::update($file, "N:U:U:U:U:U:U:U:U:U:U:U:U"); sleep 2; } my $ssCpuRawUser = getsnmp($s, ".1.3.6.1.4.1.2021.11.50.0"); my $ssCpuRawNice = getsnmp($s, ".1.3.6.1.4.1.2021.11.51.0"); my $ssCpuRawSystem = getsnmp($s, ".1.3.6.1.4.1.2021.11.52.0"); my $ssCpuRawIdle = getsnmp($s, ".1.3.6.1.4.1.2021.11.53.0"); my $ssCpuRawKernel = getsnmp($s, ".1.3.6.1.4.1.2021.11.55.0"); my $ssCpuRawInterrupt = getsnmp($s, ".1.3.6.1.4.1.2021.11.56.0 +"); print "N:$ssCpuRawUser:$ssCpuRawNice:$ssCpuRawSystem:$ssCpuRawId +le:$ssCpuRawKernel :$ssCpuRawInterrupt\n" if $debug; RRDs::update($file, join(":", "N", $ssCpuRawUser, $ssCpuRawNice, $ssCpuRawSystem, $ssCpuRawIdle, $ssCpuRawKernel, $ssCpuRawInterrupt, )); if(my $ERROR = RRDs::error) { print "ERROR: $ERROR\n"; }; $s->close(); graph($host, $file, 3600, "hour"); graph($host, $file, 86400, "day"); graph($host, $file, 86400*7, "week"); graph($host, $file, 86400*31, "month"); graph($host, $file, 86400*365, "year"); if($once) { open(INTER, '>', "$html/$host.html"); ### print html print INTER join("\n", qq(<html>), qq(<head>), qq(<title>$host</title>), qq(</head>), qq(<body>), qq(<img src="$link$host-day.png">), qq(<img src="$link$host-week.png">), qq(<img src="$link$host-month.png">), qq(<img src="$link$host-year.png">), ); close INTER; } }

        You're the unfortunate victim of poor formatting. It's calling the graph sub several times for each hostname in @host (kind of poorly named; a plural name @hosts would read better, but I digress). You just can't see the flow because someone's gotten the indentation out of whack. In cases such as this perltidy is your friend.

        The cake is a lie.
        The cake is a lie.
        The cake is a lie.

Re: Scripts with only sub-routines?
by NetWallah (Canon) on Mar 25, 2008 at 00:24 UTC
    This looks like an old, manual way to configure and run MRTG.

    If you will be doing this on a regular basis, I recommend you upgrade to cacti (Will take a considerable learning curve for installation and use, but well worth it).

         "As you get older three things happen. The first is your memory goes, and I can't remember the other two... " - Sir Norman Wisdom

Re: Scripts with only sub-routines?
by Spidy (Chaplain) on Mar 24, 2008 at 22:01 UTC
    I have two words, that embody for you everything I have learned about Perl: Embrace Magic.