2

Background

I have a perl script, called main.pl that is currently in several branched states on clear case like so:

Branch 1:

my %hash
my $variable = "a"
my $variable2 = "c"

sub codeIsOtherwiseTheSame()
....

Branch 2:

my %hash2
my $variable = "b"

sub codeIsOtherwiseTheSame()
....

Branch 3

my %hash
my $variable2 = "d"

sub codeIsOtherwiseTheSame()
....

Right now, each branch of the script has the same code. The only differences are the kind of variables that are declared and what their initialized value is. What I want to do is extract these differing variables out to a wrapper script (for each variation) so that the main script does not have to be changed. I am doing this because several users will be using this script, but have only minor differences based on their use case. Thus I want each kind of user to have their own simplified interface. At the same time, I want the main script to still be aware of these variable once it is called. Below is an example of what I want:

Desired Solution

Wrapper Script 1:

 my %hash;
 my $variable = "a";
 my $variable2 = "c";
 system("main.pl");

Wrapper Script 2:

 my %hash2;
 my $variable = "b";
 system("main.pl");

Wrapper Script 3:

my %hash;
my $variable2 = "d";
system("main.pl");

Main.pl

sub codeIsOtherwiseTheSame()

Question

How do I extract out a wrapper script to obtain the organization and behavior I want above?

isakbob
  • 1,439
  • 2
  • 17
  • 39
  • system() spawns a new process and any variables in the calling code are therefore inaccessible. To do what you want, you would have to put the variables into the environment before spawning. $ENV{variable} = "a"; etc. But the right way to do this kind of thing is with a module as choroba described. – lordadmira Nov 05 '19 at 05:16

2 Answers2

6

Extract the common code into a module, not a script. Save it as e.g. MyCommon.pm. Export a function from the module that does what you need:

package MyCommon;
use Exporter qw{ import };
our @EXPORT = qw{ common_code };

sub common_code {
    my ($var1, $var2) = @_;
    # Common code goes here...
}

Then, in various scripts, write

use MyCommon qw{ common_code };

common_code('a', 'b');  # <- insert the specific values here.

There are more advanced ways, e.g. you can use "object orientation": construct an object from the specific values, then run a method that implements the common code - but for simple use cases, you probably don't need it.

choroba
  • 231,213
  • 25
  • 204
  • 289
1

Desired behavior for simple case as yours can be achieved with with required function of perl

Put common code in a file, for example common.inc end the file with 1; (requirement for modules and include files)

sub commonFunction {
    my $data = shift;

    print "DATA: $data\n";
}

1;

Copy/move common.inc file into one of @INC directory (probably site directory best fit for this purpose).

Check your perl @INC configuration setting with following command

perl -e "print qw(@INC)"

Now you can reuse common.inc file in your user interface script

#!/usr/bin/perl

require 'common.inc';

my $a = 7;

commonFunction($a);

Already was suggested to place the common code which will be reused multiple times in form of .pm module.

By doing so you gain more control what functions/variables are visible (exported) to avoid namespace clash/collision [the modules can have functions/variables with same name].

Short tutorial how to create a module is available. Next natural step will be OOP programming.

Book: Object Oriented Perl

perlootut, Writing perl modules, Chapter Object Oriented Perl

Polar Bear
  • 6,762
  • 1
  • 5
  • 12
  • Only use `require`/`use` for files with a `package`. Use `do` for files without. But you really shouldn't have such files. This is a model that was supplanted by modules over 20 years ago! – ikegami Nov 06 '19 at 01:33
  • @ ikegami - Documentation for **do** states _It also differs in that code evaluated with 'do FILE' cannot see lexicals in the enclosing scope; 'eval STRING' does._. You should provide a sample code to prove your point. A test run of a script demonstrated that a variable from _external_ script is not accessible on _inner_ script. – Polar Bear Nov 06 '19 at 18:19
  • The documentation is 100% correct. Files included via `do`, `require` or `use` can't access lexicals in the scope in which the `do`/`require`/`use` is located. [Demonstration](https://pastebin.com/KzqinAzK) I'm not sure what that has to do with anything. – ikegami Nov 06 '19 at 19:38
  • As for why `do` is more appropriate, see how it uses `%INC` compared to `require`. – ikegami Nov 06 '19 at 19:38
  • I assume that the author of the question will use variables from _outer_ script in _inner_ common code. In case of **do** they are not accessible. – Polar Bear Nov 06 '19 at 20:42
  • If that's what you believe, then your solution doesn't work. Like I said and proved above, they're not accessible when using `require` either. In both cases, they would need to be passed as arguments. Only `eval` allows you to access the surrounding scope (and even then, there are limits). – ikegami Nov 06 '19 at 20:57
  • @ikegami - here I must agree with you. In my original test I have passed arguments and used them in code - it works fine. But then when you referred to **do** as I never used this approach I went right to read documentation. The quote I referred to earlier been caught by my eye -- and I mentioned it. A few seconds ago I run a quick test and indeed **require** hides global scope variables - it can be looked as an advantage and from other point a disadvantage. But to pass variables is more correct approach (only module might have global variable - Yahoo had such variable to extract stock quotes) – Polar Bear Nov 06 '19 at 21:33
  • It hides *lexical* vars. Global vars, by definition, are visible everywhere – ikegami Nov 06 '19 at 21:35
  • Ok, you caught me here -- I never looked before definition of lexical variable (any variable declared with **my**). Then could you demonstrate what would be **global** variable in perl? – Polar Bear Nov 06 '19 at 21:38
  • Ok, I found that only **Package** variables can have global scope -- hmm, different from C,C++. [https://stackoverflow.com/questions/19100106/local-and-global-variables-in-perl] – Polar Bear Nov 06 '19 at 21:41
  • "_different from C,C++_" ... yeah ... go figure. I can't help it but add: this post is a terrible advice in this day. It's all told in the very first comment, "_This is a model that was supplanted by modules over 20 years ago_". One can't really hash out all the whys in comments but using modules is just (far) better. (In fact, simply having "common code" be a full program that's called with suitable arguments would also be better in this case.) – zdim Nov 06 '19 at 22:12