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

Hi Monks, I need your wisdom.
Can you please suggest on the best way to determine a subroutine name in Perl file by given a line number. Say for example I have a file that has many subs of which a sub called test_sub, that occupies lines between 38 and 100.

I need the script to return "test_sub" in case the line number given is within the line range of the sub. In cases where the line number hits another sub it should return the name of that sub, etc.
  • Comment on perl determine a subroutine name by given line

Replies are listed 'Best First'.
Re: perl determine a subroutine name by given line
by moritz (Cardinal) on Aug 05, 2011 at 08:40 UTC

    This sounds suspiciously like an XY Problem - there could be a much better solution for the bigger problem you are trying to solve, but you need to tell us what that bigger problem is.

    You could of course just open the script like an ordinary file, and try to parse the line in which the sub is defined. That's not very reliable, so a totally different approach to your bigger problem might be good idea.

      Ok, I am using SciTe (http://www.scintilla.org/SciTE.html) text editor for my coding. I wrote a small script which I can call from SciTe to give me a list of my subroutines. I am able to pass the current line number as well. I need the script to also tell me what is the current subroutine the text editor is currently in. In a simple case when the subroutines are all together and there is no other code in between, it is an easy thing and I've done it. I just want to make it have better logic.

      Please have a look at my code so far:
      use strict; my $filename = $ARGV[0]; my $current_line = $ARGV[1]; my (%subs,$linecnt, %types, %indents); open (IN,$ARGV[0]); my $previous_sub; my $previous_sub_line = 0; my $current_sub_name; while (<IN>) { my $line = $_; chop($line); $linecnt ++; if ($line =~ /([\s|\t]*)(sub|def|class)[\s]+(\w+)/) { $subs{$3} = $linecnt; $types{$3} = $2; $indents{$3} = $1 if ($2 ne 'sub'); if ($current_line >= $previous_sub_line && $current_line < $li +necnt && ! $current_sub_name) { $current_sub_name = $previous_sub; } $previous_sub_line = $linecnt; $previous_sub = $3; } } close IN; $current_sub_name = $previous_sub if (!$current_sub_name && $previous_ +sub); if ($current_sub_name) { print "\n>> CURRENTLY IN:\n"; print ">> $indents{$current_sub_name}$types{$current_sub_name} [$c +urrent_sub_name] at $filename line $subs{$current_sub_name}\n"; } else { print "\n>> \n"; print ">> *** Not in a function / class block ***\n"; } print "\n"; print "FUNCTION LIST:\n"; foreach my $sub (sort {$a cmp $b} keys %subs) { print "$indents{$sub}$types{$sub} [$sub] at $filename line $subs{$ +sub}\n"; }

      When editing with SciTe a file which looks like this:
      sub test0 { print "blabla1" } sub test1 { print "blabla" }
      The output is for example:
      >perl C:\temp\showsubs.pl delme.pl 7 >> CURRENTLY IN: >> sub [test1] at delme.pl line 6 FUNCTION LIST: sub [test0] at delme.pl line 2 sub [test1] at delme.pl line 6 >Exit code: 0 Time: 0.213

      SciTe will then allow you to click on a line (say "sub test0 at delme.pl line 2") and will position the cursor in the relevant line in your code.
      In order to test this you need SciTe and the following lines in your perl.properties file:
      command.name.4.$(file.patterns.perl)=List Routines command.4.$(file.patterns.perl)=perl C:\temp\showsubs.pl $(FileNameExt +) $(SelectionStartLine)

      By having this you can press CTRL+4 in SciTe and get a function list as the above, as well as current function / sub you are in based on the current line of the editor.
        Padre depends on PPI to analyze Perl script sources. I suggest you do the same.
        Check Geany, a lightweight IDE based on scintilla which already list functions and variables and is very similar to SciTe. I use SciTe as my primary editor, but I must admit that Geany is getting better as time passes by.
Re: perl determine a subroutine name by given line
by Khen1950fx (Canon) on Aug 05, 2011 at 09:54 UTC
    I agree with moritz. You need a different approach. A good way to get started would be to use module_info from Module::Info. It will work on a module or script. For example:
    module_info -s /usr/lib/perl5/5.8.8/CGI.pm
    That will give you a list of all the subroutines in that module. Then you can get the line number of where that sub starts by using pfunc, which is also from Module::Info. Using CGI again, you can get the line number for the start of a sub, let's use 'param':
    pfunc param /usr/lib/perl5/5.8.8/CGI.pm
Re: perl determine a subroutine name by given line
by cdarke (Prior) on Aug 05, 2011 at 09:15 UTC
    The closest I can come-up with is B::Xref (in the base):
    perl -MO=Xref,d script-name.pl
    which gives you the last line-number of each subroutine definition.

      Not quite. It seems to give the line number where the subroutine becomes known to perl. Thus, if the sub is used before it is defined the line numbers will be wrong. Example:

      #!/usr/bin/perl use 5.010; say f( 34 ); sub f { $_[0] + 8 }

      Then, perl -MO=Xref,d /tmp/test.pl gives (among other things):

      Package main &f s3

      Which is the line where f is used, not defined.

      This is perl, v5.10.1 (*) built for x86_64-linux-gnu-thread-multi

      Good Day,
          Dean