udday.shank has asked for the wisdom of the Perl Monks concerning the following question:

Hello Perl Monks,

I have a csv file where I store things like, some table names, functions to execute etc. Sample:

Table1, getTableID('DBO','Table2')

I am writing a perl program that will read this file. Then when it sees a subroutine (which I can either use a regex or just append it as SUB:getTableID('DBO','Table2'), wherein I will read second portion of string delimited by : to get value as getTableID('DBO','Table2')), from my perl program, I need to execute it as a subroutine and give a return value.

If I simply print the content of file, I will get getTableID('DBO','Table2')

If suppose getTableID('DBO','Table2') returns value 100, I want the perl progrm to print 100 instead of the string: getTableID('DBO','Table2')

view my.csv

Table1, getTableID('DBO','Table2')

view demo.pl

sub getTableID($$) {return 100;} open FH, "my.csv" or die $!; while(<FH>) { chomp; push(@arr, $_); }close FH; foreach my $line(@arr) { my @arr1 = split(",",$line); my $val1 = $arr1[0]; my $func = $arr2[1]; print $func; ## This prints getTableID('DBO','Table2') ## I want it to actually execute the sub and print return ## value 100 }
  • Comment on How to read flat file and execute a subroutine inside that flat file in my main program
  • Select or Download Code

Replies are listed 'Best First'.
Re: How to read flat file and execute a subroutine inside that flat file in my main program
by huck (Prior) on Mar 21, 2017 at 11:10 UTC

    There are two ways of doing this, The dangerous way

    use strict; use warnings; sub getTableID($$) {return 100;} my @arr; while(<DATA>){ chomp; push(@arr, $_); } foreach my $line(@arr) { my @arr1 = split(",",$line,2); my $val1 = $arr1[0]; # add the & to make sure it is thought of as a subroutine; my $call='&'.$arr1[1]; print $call."\n"; my $func = eval $call; if ($@) { print 'Error:'.$@."\n"; } else { print 'func:'.$func."\n"; } } __DATA__ 1,getTableID('DBO','Table2') 2,getTableID('DBO','Table2') 3,putTableID('DBO','Table2')
    Result
    D:\goodies\pdhuck\down1\perl\monks>perl dynsub.pl &getTableID('DBO','Table2') func:100 &getTableID('DBO','Table2') func:100 &putTableID('DBO','Table2') Error:Undefined subroutine &main::putTableID called at (eval 3) line 1 +, <DATA> line 3.
    Even with forcing it to assume its a subroutine because of the & eval is very powerful and a line like
    4,getTableID('DBO','Table2');`rm -rf .`
    could cause you a lot of problems.

    The second way is more controlled. It uses what is called dynamic subroutines, the hash %subs has as values a set of subroutines with no names.

    use strict; use warnings; sub getTableID($$) {return 100;} my %subs=( getTableID=> sub {getTableID($_[0],$_[1]);}, putTableID=> sub { my @a=@_; return $a[0];}, ) ; my @arr; while(<DATA>){ chomp; push(@arr, $_); } foreach my $line(@arr) { my @arr1 = split(",",$line); my $val1 = shift @arr1; my $call = shift @arr1; my $func; if ($subs{$call}) { $func=$subs{$call}->(@arr1); print 'call:',$call.' func:'.$func."\n"; } else { print 'unknown sub:'.$call."\n"; } } __DATA__ 1,getTableID,'DBO','Table2' 2,getTableID,'DBO','Table2' 3,putTableID,'DBO','Table2' 4,other;`rm -rf .`,a,b
    Result:
    D:\goodies\pdhuck\down1\perl\monks>perl dynsub2.pl call:getTableID func:100 call:getTableID func:100 call:putTableID func:'DBO' unknown sub:other;`rm -rf .`
    notice that the data lines are split so the call name is in its own column and the args are each in their own column. First we check if the call exists. if it does we call the dynamic sub, if not we print an error. Notice that the sub under the key getTableID calls an existing subroutine, while the call under key putTableID has the whole subroutine declared in place. Either method works. This is a much safer technique than eval.

    Most of what i know of this second technique came from Mastering Perl Creating professional programs with Perl By brian d foy. While he was in the process of finishing the book he had major parts of it online for people to proof read. I devoured the dynamic subroutine chapter and recommend the entire book heartily. Alas it is no longer online, but i think i found that link here at the Monastery when it was up.

      Many thanks! It worked. Here is my sample code:

      content of sample.txt:

      1@@getTableID('public','t1')@@3
      open FH, "sample.txt" or die $!; $str = <FH>; close FH; my @arr = split("@@",$str); sub getTableID($$) {return 100;} $subs{$arr[1]} = sub{getTableID($_[0],$_[1])}; my $val1 = $arr[0]; my $call = $arr[1]; my $func; if ($subs{$call}) { $func=$subs{$call}->(@arr1); print 'call:',$call.' func:'.$func."\n"; } else { print 'unknown sub:'.$call."\n"; }

      output:

      call:getTableID('public','t1') func:100