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

I'm having an issue with a script I'm trying to write, I am writing a CLI menu system, where the menu definitions are being stored in an XML file. I am trying to pass a subroutine to the script to run, but I keep getting an Undefined subroutine, even though it exists within the same module its being processed from. Can someone determine what I'm doing wrong? Why is MenuNA being undefined?

script I'm running from (ScriptLibsMenu):

#!/usr/bin/perl use strict; use lib ("/home/jsmith/lib"); use ScriptLibs::Menu; my $DEBUG='1'; my $MENUFILE='/home/jsmith/etc/MENU.example.main'; my $MENU=''; MenuGen($DEBUG,"$MENUFILE");

Module I'm calling (ScriptLibs::Menu.pm):

package ScriptLibs::Menu; # VERSION: 1444685500 #use Time::localtime; #use File::Basename; use strict; use warnings; #################### subroutine header begin #################### =head1 NAME: ScriptLibs::Utils =head1 SYNOPSIS: use ScriptsList::Func_DEBUG; Func_DEBUG("DEBUGCONTROL","DEBUGMESSAGELEVEL","DEBUGMESSAGE"); =head1 DESCRIPTION: DEBUG Utility used with Scripts =head1 USAGE: =cut #################### subroutine header end #################### BEGIN { use Exporter (); use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); $VERSION = '1444685500'; @ISA = qw(Exporter); #Give a hoot don't pollute, do not export more than needed by defa +ult @EXPORT = qw(MenuGen MenuNA); @EXPORT_OK = qw(all); # %EXPORT_TAGS = (all => [qw(test_one test_two)]); %EXPORT_TAGS = ( Functions => [ qw(MenuGen MenuNA) ], Variables => [ qw($FUNCNAME $HOSTNAME $LOGDATE $VE +RSION) ], ); } use File::Basename; use IO::File; use XML::Simple qw(:strict); my $CLEAR=`clear`; my $clear=`clear`; my $DEBUG=''; my $SRCNAME=''; my $ETCPATH=''; my $FUNCNAME=''; my $TAILLINE=''; my $LIBNAME="ScriptLibs::Menu"; my $MENUBOB='1'; use ScriptLibs::Debug('Func_DEBUG'); use ScriptLibs::ScrIO('Func_Error','Func_Entry','Func_Pause'); use ScriptLibs::Usage('Func_Usage'); sub MenuNA { #NOTE: Not Available my $FUNCNAME='MenuNA'; Func_Pause("Menu Option is not available in this release"); } #END MenuNA; sub Func_MenuReturn { #NOTE: Exits the script my $FUNCNAME='Func_MenuReturn'; print "FUNCNAME=$FUNCNAME"; $MENUBOB='2'; } #END MenuNA; sub Func_MenuExit { #NOTE: Exits the script my $FUNCNAME='Func_MenuExit'; exit; } #END Func_MenuExit; sub Func_MenuLogout { #NOTE: Exits the script my $FUNCNAME='Func_MenuLogout'; my $SCRIPTNAMETEMP=$0; my $SCRIPTNAME=basename($SCRIPTNAMETEMP); my $PSEARCH1=''; my $PTY=''; my $PSEARCH2=''; #print "SCRIPTNAME=$SCRIPTNAME\n"; my @PS1=split(/\n/, `/bin/ps -ef | /bin/grep $SCRIPTNAME`); foreach my $line (@PS1) { chomp $line; #print "line=$line\n"; if ( $line =~ /sh -c \/bin\/ps -ef/ && $line =~/$SCRIPTNAME/ ) + { my @ARRAY=split(/\s+/, $line); $PSEARCH1=$ARRAY[2]; $PTY=$ARRAY[5]; last; } else { next; } } #print "PSEARCH1=$PSEARCH1\n"; foreach my $line2 (@PS1) { chomp $line2; my @ARRAY2=split(/\s+/, $line2); if ( $ARRAY2[1] =~ /$PSEARCH1/ && $ARRAY2[5] =~/$PTY/ ) { #print "line2=$line2\n"; $PSEARCH2=$ARRAY2[2]; last; } else { next; } } #print "PSEARCH2=$PSEARCH2\n"; my @PS2=split(/\n/, `/bin/ps -ef | /bin/grep $PTY`); foreach my $line3 (@PS2) { chomp $line3; #print "line3=$line3\n"; my @ARRAY3=split(/\s+/, $line3); if ( $ARRAY3[1] =~ /^$PSEARCH2$/ && $ARRAY3[5] =~ /$PTY/ ) { my $YESNO=Func_Entry("$DEBUG","Are you sure you wish to lo +gout?","(Y/N)","N","menu"); chomp $YESNO; if ( uc($YESNO) =~ /^Y/ ) { `/usr/bin/kill -9 $PSEARCH2`; } last; } else { next; } } } #END Func_MenuLogout; sub MenuGen { #NOTE: Generates a Menu my $FUNCNAME='MenuGen'; my $MENUFILE=''; my $MENU=''; my %COMMANDS; my %HELPS; my $CURSOR; if ($_[0]) { $DEBUG=$_[0]; chomp $DEBUG; } if ($DEBUG) { Func_DEBUG("$DEBUG","1","$FUNCNAME"); } #DEBUG if ($_[1]) { $MENUFILE=$_[1]; chomp $MENUFILE; } else { Func_Error("1","Must supply a menufile to $FUNCNAME"); } if ( -e "$MENUFILE" ) { #print "MENUFILE=$MENUFILE\n"; $MENU=XMLin($MENUFILE, forcearray => [ qw(OPTION SELECT TEXT C +OMMAND HELP) ], keyattr => [] ); #use Data::Dumper; #print Dumper($MENU); my $MENUBOB='1'; while ($MENUBOB eq '1') { print "$CLEAR"; print "$MENU->{TITLE}\n"; foreach my $SUB ($MENU->{SUBTITLE}) { chomp $SUB; if ( $SUB =~ /ARRAY/ ) { foreach my $BOB (@$SUB) { print " $BOB\n"; } } else { print " $SUB\n"; } } #END foreach my $SUB ($MENU->{SUBTITLE}) print "\n"; foreach my $MENOPT ($MENU->{OPTION}) { chomp $MENOPT; my $SELECT=''; my $TEXT=''; my $POPHELP=''; my $POPCOMM=''; foreach my $BOB (@$MENOPT) { foreach my $BOB2 (keys (%$BOB)) { chomp $BOB2; #print "BOB2=$BOB2 $$BOB{$BOB2}->[0]\n"; if ( $BOB2 =~ /SELECT/ ) { $SELECT=$$BOB{$BOB2}->[0]; chomp $SELECT; next; } elsif ( $BOB2 =~ /TEXT/ ) { $TEXT=$$BOB{$BOB2}->[0]; chomp $TEXT; next; } elsif ( $BOB2 =~ /HELP/ ) { $POPHELP=$$BOB{$BOB2}->[0]; chomp $TEXT; next; } elsif ( $BOB2 =~ /COMMAND/ ) { $POPCOMM=$$BOB{$BOB2}->[0]; chomp $TEXT; next; } else { } } #END foreach my $BOB2 (keys (%$BOB)) if ( $SELECT =~ /^H/ ) { print "\n $SELECT - $TEXT\n"; $COMMANDS{"$SELECT"}="\\\&$POPCOMM"; $HELPS{"$SELECT"}="$POPHELP"; } elsif ( $SELECT =~ /^X/ ) { print " $SELECT - $TEXT\n\n"; $COMMANDS{"$SELECT"}="$POPCOMM"; $HELPS{"$SELECT"}="$POPHELP"; } else { print " $SELECT - $TEXT\n"; $COMMANDS{"$SELECT"}="\\\&$POPCOMM"; $HELPS{"$SELECT"}="$POPHELP"; } } #END foreach my $BOB (@$MENOPT) } #END foreach my $MENOPT ($MENU->{OPTION}) $CURSOR=$MENU->{'CURSOR'}; my $ENTRY=Func_Entry("$DEBUG","Enter your selection","$CU +RSOR","X","menu"); chomp $ENTRY; if ($ENTRY =~ /^[0-9]/ ) { print "$COMMANDS{$ENTRY}\n"; if ( $COMMANDS{$ENTRY} =~ /SUBMENU/ ) { my @ARRAY=split(/\s+/, $COMMANDS{$ENTRY}); my $SUBMENUFILE=$ARRAY[1]; $SUBMENUFILE=~s/^SUBMENU //; MenuGen($DEBUG,"$SUBMENUFILE"); } else { my $RUNNCOMMAND=$COMMANDS{$ENTRY}; my $RUNBOB=\&$RUNNCOMMAND; &$RUNBOB(); } } elsif ( $ENTRY =~ /^H/i ) { my $HELPME=$ENTRY; $HELPME=~s/^[Hh]//; Func_Usage("$HELPS{$HELPME}"); } elsif ( $ENTRY =~ /^x/i || $ENTRY =~ /^q/i ) { my $RUNNCOMMAND=$COMMANDS{'X'}; #print "RUNNCOMMAND=$RUNNCOMMAND\n";; if ( $RUNNCOMMAND =~ /return/i ) { $MENUBOB='2'; } elsif ( $RUNNCOMMAND =~ /exit/i ) { exit; } elsif ( $RUNNCOMMAND =~ /logout/i ) { Func_MenuLogout(); } else { } } else { Func_Error('2',"You have entered an invalid option"); } } #END while ($BOB eq '1') } else { Func_Error("1","Menu definitiion file $MENUFILE does not exist +"); } } #END MenuGen #n pod documentation begin ################### ## Below is the stub of documentation for your module. ## You better edit it! #=head1 NAME #Test - Blank Module #=head1 SYNOPSIS # use Test; # blah blah blah #=head1 DESCRIPTION # #Stub documentation for this module was created by ExtUtils::ModuleMak +er. #It looks like the author of the extension was negligent enough #to leave the stub unedited. #Blah blah blah. # #=head1 USAGE #=head1 BUGS #=head1 SUPPORT #=head1 HISTORY #0.01 Fri Aug 29 10:12:08 2008 # - original version; created by ExtUtils::ModuleMaker 0.51 # #=head1 AUTHOR # James M Smith #=head1 COPYRIGHT #This program is free software; you can redistribute #it and/or modify it under the same terms as Perl itself. #The full text of the license can be found in the #LICENSE file included with this module. #=head1 SEE ALSO #perl(1). #=cut #################### main pod documentation end ################### 1; # The preceding line will help the module return a true value

The XML file (MAIN.example.main)

<MENU> <TITLE>Configuration Wizard </TITLE> <SUBTITLE>Enter a menu option to begin</SUBTITLE> <OPTION> <SELECT>1</SELECT> <TEXT>Configure System Administrator Password</TEXT> <COMMAND>1</COMMAND> <HELP>PASSWD CHANGE \n Change the Administrator password</HELP> </OPTION> <OPTION> <SELECT>2</SELECT> <TEXT>Configure Network Parameters</TEXT> <COMMAND>SUBMENU /home/jsmith/etc/MENU.example.NetConfig</COMMAND +> <HELP>Set the IP, netmask, gateway, and configure various network + services</HELP> </OPTION> <OPTION> <SELECT>3</SELECT> <TEXT>Test TCP Network Settings</TEXT> <COMMAND>11</COMMAND> <HELP>Ping, nslookup, etc</HELP> </OPTION> <OPTION> <SELECT>4</SELECT> <TEXT>Manage Processes</TEXT> <COMMAND>11</COMMAND> <HELP>Start, Stop KO</HELP> </OPTION> <OPTION> <SELECT>5</SELECT> <TEXT>Manage Physical Server</TEXT> <COMMAND>Reboot</COMMAND> <HELP>Reboot, Power cycle, manage physical drives</HELP> </OPTION> <OPTION> <SELECT>6</SELECT> <TEXT>Shell (sh)</TEXT> <COMMAND>MenuNA</COMMAND> <HELP>Not available at this time.</HELP> </OPTION> <OPTION> <SELECT>H#</SELECT> <TEXT>Help(number) for more details</TEXT> <COMMAND>11</COMMAND> </OPTION> <OPTION> <SELECT>X</SELECT> <TEXT>Exit</TEXT> <COMMAND>Logout</COMMAND> </OPTION> <CURSOR>MAIN</CURSOR> </MENU>

The error that I'm receiving:

Configuration Wizard Enter a menu option to begin 1 - Configure System Administrator Password 2 - Configure Network Parameters 3 - Test TCP Network Settings 4 - Manage Processes 5 - Manage Physical Server 6 - Shell (sh) H# - Help(number) for more details X - Exit Enter your selection: MAIN> 6 \&MenuNA Undefined subroutine &main::\&MenuNA called at /home/jsmith/lib/Script +Libs/Menu.pm line 261, <> line 1.

Replies are listed 'Best First'.
Re: Subroutine not being found when being called from within its own module.
by Anonymous Monk on Oct 13, 2015 at 22:42 UTC
      &main::\&MenuNA is not the same as   &main::MenuNA

    You're confusing a reference with a string , if you're using a string there is no \&

    $ perl -le"sub Yoda::Says { warn join q/ / , @_ }; q{Yoda::Says}->( +q/hello/ ) ; " hello at -e line 1. $ perl -le"sub Yoda::Says { warn join q/ / , @_ }; q{Yoda::&Says}->( + q/hello/ ) ; " Undefined subroutine &Yoda::&Says called at -e line 1.

      HA! Thanks for the second set of eyes! I caused this problem myself! <I'm such a maroon>

        It happens to everybone :D
Re: Subroutine not being found when being called from within its own module.
by kcott (Archbishop) on Oct 13, 2015 at 22:58 UTC

    G'day curucahm,

    Welcome to the Monastery.

    Your problem is package-related. The error message indicates that Perl is looking for your subroutine in main (the default package); however, your code has that subroutine in package ScriptLibs::Menu.

    If you don't know about packages, see the documentation in package for basic information. It has links to further details: follow them as required.

    [Aside: You've included far too much code in your post. Most of it does not relate to the problem at hand. I only read the error message and then looked for "sub MenuNA" in your code. It's better to post a short piece of code that reproduces your problem; in fact, doing this will often highlight the problem for you. Please see "How do I post a question effectively?" for more discussion regarding this. When you do need to post "pages" of code, please use <spoiler> or <readmore> tags: "Writeup Formatting Tips" has more about this.]

    Update: Having seen AM's response, my "problem is package-related" is wrong. I have stricken that part of my post. Please ignore.

    — Ken