[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 445
  • Last Modified:

Ways of calling another perl script

I understand there are multiple ways to call another script within a perl script - system(), require(), use(), do().

Looking at the man page, my opinion seems like require() is preferred. On a modern perl perspective, would that be true?

In my example, I have many scripts that would like to call a email script with NET::SMTP, sendmail.pl.

sendmail.pl
#!/usr/bin/perl
#
use Net::SMTP;

my $infile = 'body.txt';
open(INFILE,"< $infile");
@file = <INFILE>;
close INFILE;
my $smtp_host = 'smtp.example.com';
my $from = "host1";
my $to = 'receipient@example.com';
if (length($ENV{HOSTNAME} ne "")) {$machine = $ENV{HOSTNAME};}
else {$machine = $ENV{COMPUTERNAME};}
my $subject = "Server(s) with corrupt file system records";
my $smtp = Net::SMTP->new($smtp_host,Timeout=>30,Debug=>1);
$smtp->mail($from);
$smtp->to($to);
$smtp->data();
$smtp->datasend(<<END_OF_MESSAGE);
To: $to
#Bcc: $to
From: $from
Subject: $subject

@file
END_OF_MESSAGE
$smtp->datasend;
$smtp->quit; 

Open in new window

0
Mazdajai
Asked:
Mazdajai
  • 6
  • 6
1 Solution
 
wilcoxonCommented:
None of the above.  The preferred method for such a thing would be to create a module and use it.
## this is file Send/Mail.pm
package Send::Mail;

use strict;
use warnings;
use Net::SMTP;

my $smtp_host = 'smtp.example.com';

sub sendmail {
my ($infile, $from, $to) = @_;
$infile //= 'body.txt';
$from //= 'host1';
$to //= 'recipient@example.com';
open INFILE,"< $infile";
my @file = <INFILE>;
close INFILE;
my $machine = $ENV{HOSTNAME} ? $ENV{HOSTNAME} : $ENV{COMPUTERNAME};
my $subject = "Server(s) with corrupt file system records";
my $smtp = Net::SMTP->new($smtp_host,Timeout=>30,Debug=>1);
$smtp->mail($from);
$smtp->to($to);
$smtp->data();
$smtp->datasend(<<END_OF_MESSAGE);
To: $to
#Bcc: $to
From: $from
Subject: $subject

@file
END_OF_MESSAGE
$smtp->datasend;
$smtp->quit;
}

1;

Open in new window

#!/usr/bin/perl
## this is your script file
use strict;
use warnings;
use lib '/path/to/Send/Mail.pm/if/not/in/standard/lib/loc';
use Send::Mail;
# ...
Send::Mail->sendmail($filename, $from, $to);

Open in new window

0
 
MazdajaiAuthor Commented:
Can I comment these variables if I want to pass them on the new script?

my $subject = "Server(s) with bad records";

$infile //= 'body.txt';
$from //= 'host1';
$to //= 'recipient@example.com';

Open in new window

0
 
wilcoxonCommented:
You already can pass them.  //= will only assign the value if the variable is undef.  I set the sendmail sub up to accept $infile, $from, and $to (in that order) as optional arguments (the line ... = @_).
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
MazdajaiAuthor Commented:
Sorry for the dealy, but it didn't work.

If I don't set it in the module, the subject would be blank and does not takes $subject in the script.

If I leave $subject in the module, it will use it regardless if I set $subject in the new script. Is this a scoping issue?

perl sendq_module.pl
Name "main::subject" used only once: possible typo at sendq_module.pl line 8.
Name "main::infile" used only once: possible typo at sendq_module.pl line 12.
readline() on closed filehandle INFILE at D:\scripts\Perl\my/mailhost1.pm line 14.
Net::SMTP>>> Net::SMTP(2.31)
Net::SMTP>>>   Net::Cmd(2.29)
Net::SMTP>>>     Exporter(5.67)
Net::SMTP>>>   IO::Socket::INET(1.33)
Net::SMTP>>>     IO::Socket(1.34)
Net::SMTP>>>       IO::Handle(1.33)
Net::SMTP=GLOB(0x27f2438)<<< 220 smtp.example.com ESMTP Service (IBM Domino Release 9.0.1FP1 HF74) ready at Fri, 15 Aug 2014 20:39:32 -040
0
Net::SMTP=GLOB(0x27f2438)>>> EHLO localhost.localdomain
Net::SMTP=GLOB(0x27f2438)<<< 250-smtp.example.com Hello localhost.localdomain ([10.10.45.156]), pleased to meet you
Net::SMTP=GLOB(0x27f2438)<<< 250-HELP
Net::SMTP=GLOB(0x27f2438)<<< 250-SIZE 94371840
Net::SMTP=GLOB(0x27f2438)<<< 250 PIPELINING
Net::SMTP=GLOB(0x27f2438)>>> MAIL FROM:<director@example.com>
Net::SMTP=GLOB(0x27f2438)<<< 250 director@example.com... Sender OK
Net::SMTP=GLOB(0x27f2438)>>> RCPT TO:<john.doe@example.com>
Net::SMTP=GLOB(0x27f2438)<<< 250 john.doe@example.com... Recipient OK
Net::SMTP=GLOB(0x27f2438)>>> DATA
Net::SMTP=GLOB(0x27f2438)<<< 354 Enter message, end with "." on a line by itself
Use of uninitialized value $Send::Mail::subject in concatenation (.) or string at D:\scripts\Perl\my/mailhost1.pm line 22.
Net::SMTP=GLOB(0x27f2438)>>> To: john.doe@example.com
Net::SMTP=GLOB(0x27f2438)>>> #Bcc: john.doe@example.com
Net::SMTP=GLOB(0x27f2438)>>> From: director@example.com
Net::SMTP=GLOB(0x27f2438)>>> Subject:
Net::SMTP=GLOB(0x27f2438)>>> .
Net::SMTP=GLOB(0x27f2438)<<< 250 Message accepted for delivery
Net::SMTP=GLOB(0x27f2438)>>> QUIT
Net::SMTP=GLOB(0x27f2438)<<< 221 smtp.example.com SMTP Service closing transmission channel

perl sendq_module.pl
Name "main::subject" used only once: possible typo at sendq_module.pl line 8.
Name "main::infile" used only once: possible typo at sendq_module.pl line 12.
readline() on closed filehandle INFILE at D:\scripts\Perl\my/mailhost1.pm line 14.
Net::SMTP>>> Net::SMTP(2.31)
Net::SMTP>>>   Net::Cmd(2.29)
Net::SMTP>>>     Exporter(5.67)
Net::SMTP>>>   IO::Socket::INET(1.33)
Net::SMTP>>>     IO::Socket(1.34)
Net::SMTP>>>       IO::Handle(1.33)
Net::SMTP=GLOB(0x25b2450)<<< 220 smtp.example.com ESMTP Service (IBM Domino Release 9.0.1FP1 HF74) ready at Fri, 15 Aug 2014 20:40:21 -040
0
Net::SMTP=GLOB(0x25b2450)>>> EHLO localhost.localdomain
Net::SMTP=GLOB(0x25b2450)<<< 250-smtp.example.com Hello localhost.localdomain ([10.10.45.156]), pleased to meet you
Net::SMTP=GLOB(0x25b2450)<<< 250-HELP
Net::SMTP=GLOB(0x25b2450)<<< 250-SIZE 94371840
Net::SMTP=GLOB(0x25b2450)<<< 250 PIPELINING
Net::SMTP=GLOB(0x25b2450)>>> MAIL FROM:<director@example.com>
Net::SMTP=GLOB(0x25b2450)<<< 250 director@example.com... Sender OK
Net::SMTP=GLOB(0x25b2450)>>> RCPT TO:<john.doe@example.com>
Net::SMTP=GLOB(0x25b2450)<<< 250 john.doe@example.com... Recipient OK
Net::SMTP=GLOB(0x25b2450)>>> DATA
Net::SMTP=GLOB(0x25b2450)<<< 354 Enter message, end with "." on a line by itself
Net::SMTP=GLOB(0x25b2450)>>> To: john.doe@example.com
Net::SMTP=GLOB(0x25b2450)>>> #Bcc: john.doe@example.com
Net::SMTP=GLOB(0x25b2450)>>> From: director@example.com
Net::SMTP=GLOB(0x25b2450)>>> Subject: Server(s) with corrupt file system records
Net::SMTP=GLOB(0x25b2450)>>> .
Net::SMTP=GLOB(0x25b2450)<<< 250 Message accepted for delivery
Net::SMTP=GLOB(0x25b2450)>>> QUIT
Net::SMTP=GLOB(0x25b2450)<<< 221 smtp.example.com SMTP Service closing transmission channel

Open in new window

0
 
wilcoxonCommented:
Try these minor modifications.  If you want to pass in further values, you can just make similar changes.  The one mistake I made was that it should be Send::Mail::sendmail in your script (not Send::Mail->sendmail).
#!/usr/bin/perl
## this is your script file
use strict;
use warnings;
use lib '.';
use Send::Mail;
# ...
Send::Mail::sendmail('somefile.txt', 'me@somewhere.com', 'you@elsewhere.com');

Open in new window

## this is file Send/Mail.pm
package Send::Mail;

use strict;
use warnings;
use Net::SMTP;

my $smtp_host = 'smtp.example.com';

sub sendmail {
my ($infile, $from, $to, $subject) = @_;
$infile //= 'body.txt';
$from //= 'host1';
$to //= 'recipient@example.com';
$subject //= 'Server(s) with corrupt file system records';

#print "infile = $infile\nfrom = $from\nto = $to\nsubject = $subject\n";
#return;

open INFILE,"< $infile";
my @file = <INFILE>;
close INFILE;
my $machine = $ENV{HOSTNAME} ? $ENV{HOSTNAME} : $ENV{COMPUTERNAME};
my $subject = "Server(s) with corrupt file system records";
my $smtp = Net::SMTP->new($smtp_host,Timeout=>30,Debug=>1);
$smtp->mail($from);
$smtp->to($to);
$smtp->data();
$smtp->datasend(<<END_OF_MESSAGE);
To: $to
#Bcc: $to
From: $from
Subject: $subject

@file
END_OF_MESSAGE
$smtp->datasend;
$smtp->quit;
}

1;

Open in new window

0
 
MazdajaiAuthor Commented:
I am not sure if Windows support the naming convention. (Can't locate Send/Mail.pm in @INC)

If I do not disable strict, I will get some compilation and declaraton errors:
"my" variable $subject masks earlier declaration in same scope at D:\scripts\Perl\my/mailhost1.pm line 19.
Global symbol "$subject" requires explicit package name at sendq_module.pl line 8.
Global symbol "$from" requires explicit package name at sendq_module.pl line 10.
Global symbol "$to" requires explicit package name at sendq_module.pl line 11.
Global symbol "$infile" requires explicit package name at sendq_module.pl line 12.
Global symbol "$from" requires explicit package name at sendq_module.pl line 12.
Global symbol "$to" requires explicit package name at sendq_module.pl line 12.
Global symbol "$subject" requires explicit package name at sendq_module.pl line 12.
Execution of sendq_module.pl aborted due to compilation errors.

Open in new window


I disabled strict and renamed the package but still got the same error.
perl sendq_module.pl
#!/usr/bin/perl

#use strict;
use warnings;
use lib 'D:\scripts\Perl\my';
use mailhost1;

$subject="test subject";
#$infile='D:\scripts\Perl\getErr\bodyq.txt';
$from='john.doe@example.com';
$to=john.doe@example.com';
Send::Mail::sendmail($infile, $from, $to, $subject);

"my" variable $subject masks earlier declaration in same scope at D:\scripts\Perl\my/mailhost1.pm line 19.
Name "main::infile" used only once: possible typo at sendq_module.pl line 12.
readline() on closed filehandle INFILE at D:\scripts\Perl\my/mailhost1.pm line 16.

Open in new window

0
 
wilcoxonCommented:
You should never disable strict.  If strict is giving you errors then there is a problem in your script.  With strict enabled, you need to put "my" before your variable definitions (eg my $subject = "test subject";).  Windows certainly supports normal perl module naming (eg Send::Mail translates to Send/Mail.pm) if you are on a vaguely recent version of perl.

What does perl -v report as your version?  I think it's recent since it appear to recognize the //= operator.

If you renamed Send::Mail to mailhost1 then you need to change the package line to "package mailhost1" and "Send::Mail::sendmail" needs to become "mailhost1::sendmail".
0
 
MazdajaiAuthor Commented:
How can I create a module name with forward slash? In Windows - <>:"?||?* are not allowed in the file name.

It is running 5.16 active perl. If I change it to mailhost1::sendmail I got an undefined subroutine error. I will try to change the module name tomorrow.
0
 
wilcoxonCommented:
You would use backslash at the OS level.  Perl on Windows automatically translates forward to back slash (though using back-slashes directly should also work such as you have in "use lib").

It's possible the issue is something with active perl (I'm not a fan) but it should work.  If you have time and want to try a different perl, I recommend Strawberry Perl on Windows.
0
 
MazdajaiAuthor Commented:
Do you have an example? I am not convinced that they are allowed in Windows.

http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx

Use any character in the current code page for a name, including Unicode characters and characters in the extended character set (128–255), except for the following:

    The following reserved characters:
        < (less than)
        > (greater than)
        : (colon)
        " (double quote)
        / (forward slash)
        \ (backslash)
        | (vertical bar or pipe)
        ? (question mark)
        * (asterisk)

Integer value zero, sometimes referred to as the ASCII NUL character.

Open in new window


Thanks for the suggestion. Migrating to strawberry perl is an on going effort in our environment but I would like to assign points and close out this question first. (Unless this is a hard requirement to make it work?)
0
 
wilcoxonCommented:
The path within Windows does need to use back-slashes (the path within perl should not).  In your case, given the code above, the setup could be:
path in Windows = D:\scripts\Perl\my\Send\Mail.pm
use lib 'D:/scripts/Perl/my'; use Send::Mail;

I just tested and, in Strawberry Perl, use lib works with both D:\path\to and D:/path/to.  One thing you do need to make sure of is, if using backslashes, then the use lib must use single quotes (use lib "D:\path\to" would become D:, escape-p, ath, escape-t, o).

The warning about $subject on line 24 is my fault - I forgot to delete the original "my $subject" line when switching it to be passed in.  You can delete line 24 ("my $subject" in between the lines for "my $machine" and "my $smtp").
0
 
MazdajaiAuthor Commented:
Thank you! I misunderstand your explaination - I thought you mean slash in the file name.

Forward slash and backslash works in the path name either way.
0

Featured Post

Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

  • 6
  • 6
Tackle projects and never again get stuck behind a technical roadblock.
Join Now