Reputation: 5713
I have a simple Perl script that uses helper functions located in another file common.pl
:
#!/usr/bin/perl
use strict;
use warnings;
use Cwd 'abs_path';
use File::Basename qw( fileparse );
use File::Path qw( make_path );
use File::Spec;
require "common.pl"; # line 15
#...
#!/usr/bin/perl
use strict;
use warnings;
sub getTimeLoggerHelper{
#....
}
1;
Locally everything runs well, but when I try to run it through ssh I get an error:
Can't locate common.pl in @INC (@INC contains:
/Library/Perl/5.18/darwin-thread-multi-2level /Library/Perl/5.18
/Network/Library/Perl/5.18/darwin-thread-multi-2level
/Network/Library/Perl/5.18
/Library/Perl/Updates/5.18.2/darwin-thread-multi-2level
/Library/Perl/Updates/5.18.2
/System/Library/Perl/5.18/darwin-thread-multi-2level
/System/Library/Perl/5.18
/System/Library/Perl/Extras/5.18/darwin-thread-multi-2level
/System/Library/Perl/Extras/5.18 .) at /Users/snaggs/scripts/main.pl line 15.
If I log in to a remote machine and run the same script, then there are no errors.
I also tried converting common.pl
to a module:
package Common;
use strict;
use warnings;
sub getTimeLoggerHelper{
#....
}
1;
__END__
and from main.pl
I call it like this
use Module::Load;
load Common;
Same problem, locally works, from ssh - same error:
Can't locate Common.pm in @INC (you may need to install the Common module)
How to get rid of this problem?
Upvotes: 4
Views: 18029
Reputation: 66964
If you need to use require
for this, supply the full path
require "/full/path/to/common.pl";
This is needed over ssh
even if the file is in the same directory with the script, because over ssh
the .
in @INC
is not the script's directory (since the working directory, .
, is likely your $HOME
). This happens in other cases, as well.†
Note that this way you are importing everything that is in common.pl
.
There are many advantages to using a proper module instead. Then the file is .pm
and by convention the file name is capitalized (and in PascalCase).
Here is a bare-bones example with files bin/main.pl
and lib/Common.pm
bin/main.pl
use warnings;
use strict;
use FindBin 1.51 qw($RealBin); # Directory in which this script is
use lib "$RealBin/../lib"; # Where modules are, relative to $RealBin
use Common qw(test_me);
test_me();
The critical part of setting up @INC
, where modules are looked for,
is done with lib pragma. It adds directories to the beginning of the default @INC
, at compile time. The FindBin's $RealBin
is the script's directory, with links resolved. We use it so that added paths are relative to the script and not hard-coded. This helps source organization when script and its libraries come together.
Another way to set this up is via the environment variable PERL5LIB
. With bash
export PERL5LIB=/path/to/libdir
Then for a Module.pm
that is in that libdir
you only need to say use Module
and it will be found. This is useful for modules that live at a particular location, to be used by various scripts.
lib/Common.pm
package Common;
use strict;
use warnings;
use Exporter qw(import);
our @EXPORT_OK = qw( test_me );
sub test_me { print "Hello from ", __PACKAGE__, "\n" }
1;
When a package is used its file is first required and then the module's import method runs, at compile time. It is via import
that the caller actually gets the symbols (names for functions and variables) defined in the module, and we must provide an import
method in our module (or use fully qualified names, Module::function
, in a caller).
The line with use Exporter
brings in its import
routine, so we don't have to write our own. With old versions of Exporter this was used via inheritance, often by @ISA = ('Exporter')
. See docs.
Then symbols are made available via @EXPORT_OK
. This requires a caller to list functions to be used; it does not "push" anything into their namespace by default. There is also %EXPORT_TAG
which is helpful, specially if the list of symbols that callers import grows long.
† This is really just one example of a general problem with using the current working directory (.
) for the script's directory: it isn't; in general it just isn't.
Another thing to note, since version 5.26 the .
(current directory) is not anymore in @INC
, for security reasons. This may have also been backported to earlier versions.
So just use $RealBin
, like shown (the version 1.51 had an important fix).
Upvotes: 9