Getting started with Perl, Part 2

RELATED TOPICS

Unix Insider –

Based on some excellent suggestions from a few readers, I am making a change in the format of the listings and listing explanations in these articles. One reader suggested that listings would be easier to cut and paste if they did not include line numbers. Another pointed out that a long explanation of a long listing causes the reader to have to flip up and down the screen to refer to the text of the explanation and then the text of the listing. To accommodate both of these very good suggestions, I have changed the listing/explanation format to start with a description of what the program or listing does, followed by a complete copy of the listing without line numbers. If the program requires a further explanation broken down line by line, the unnumbered full listing will be followed by a detailed explanation composed of alternating explanations and line numbered listing fragments in the text. This should handle both problems, and, I believe, will improve the usefulness and readability of these articles. Let me know what you think.

In the last issue we took Perl logic up to the point of generating a simple menu program which I repeat here in the following listing:

<font face="Courier"> 1	#!/usr/bin/perl
 2	
 3	
 4	#---------------------------------------
 5	# MAIN ROUTINE
 6	#---------------------------------------
 7	# Display a menu and get a selection
 8	get_menu_pick();
 9	
10	# as long as the E(x)it option is not chosen,
11	# execute the menu option and then display
12	# the menu again and ask for another choice
13	
14	while ( $pick ne "x" )
15	{
16		do_pick();
17		get_menu_pick();
18	}
19	
20	# clear the screen and exit with a 0 return code
21	clear_screen();
22	
23	exit (0);
24	#---------------------------------------
25	# MAIN ROUTINE ENDS
26	#---------------------------------------
27	
28	# Clear the screen, Show the menu and get user input
29	sub get_menu_pick
30	{
31		clear_screen();
32		show_menu();
33		get_pick();
34	}
35	
36	# Clear the screen by printing 25 newlines
37	sub clear_screen
38	{
39		for ($i=0; $i < 25; ++$i){
40			print "\n";
41		}
42	}
43	
44	# Open menufile.txt or exit with an error
45	# read in each row picking up the first two fields by 
46	# splitting it on the pipe |
47	# print the first two fields
48	# send some form feeds to do some centering
49	sub show_menu
50	{
51		$count = 0;
52		open( MENUFILE, "menufile.txt") or die "Can't open menufile.txt: $!\n";
53		while ($menurow=<MENUFILE>)
54		{
55			($menupick,$menuprompt)=split /:/,$menurow;
56			print "\t$menupick\t$menuprompt \n";
57			++$count;
58		}
59		close MENUFILE;
60		print "\tx\tExit\n";
61		++$count;
62		$count = (24 - $count ) / 2;
63		for ($i=0; $i < $count; ++$i){
64			print "\n";
65		}
66		print "\n\nEnter your selection\n";
67		
68	}
69	
70	# get user input and chop off the newline
71	sub get_pick()
72	{
73		chomp($pick = <STDIN>);
74	}
75	
76	sub do_pick()
77	{
78	
79		open( MENUFILE, "menufile.txt") or die "Can't open menufile.txt: $!\n";
80		while ($menurow=<MENUFILE>)
81		{
82			($menupick, $menuprompt, $menucommand)=split /:/,$menurow;
83			if ($menupick eq $pick)
84			{
85				system $menucommand;
86				break;
87			}
88		}
89		close MENUFILE;
90		press_enter();
91	}
92	
93	# put up a message and wait for user to press ENTER
94	sub press_enter
95	{
96		print "Press Enter to Continue . . .\n";
97		$dummy = <STDIN>;
98	}
99	
</font>

This menu program produced a screen something like the following by using a

<font face="Courier">menufile.txt</font>
containing menu selections:

<font face="Courier">        a       Say Hello Gracie
        b       Show Perl man pages
        c       Show Current Directory
        x       Exit


Enter your selection
</font>

The

<font face="Courier">menufile.txt</font>
is repeated in the following listing:

<font face="Courier">a:Say Hello Gracie:echo "Hello Gracie"
b:Show Perl man pages:man perl
c:Show Current Directory:ls -l|more
</font>

Adding Unix shell commands

The first step is to extend this simple menu program to allow a user to execute Unix shell commands, which the program can already do, as well as Perl functions internal to a Perl script. There is a different syntax to calling a Perl function, so the

<font face="Courier">menufile.txt</font>
must identify when a menu selection is a system request and when it is a Perl function request. To do this, a fourth field, containing a flag indicating whether the menu request is for a system call or a Perl function, must be added to the menu file. An example of this is shown in the following display of the new
<font face="Courier">menufile.txt</font>
. Create a new version of
<font face="Courier">menufile.txt</font>
, or modify the one you created for last month's article, so that it matches this illustration.

<font face="Courier">a:Say Hello Gracie:echo "Hello Gracie":system
b:Show Perl man pages:man perl:system
c:Show Current Directory:ls -l|more:system
d:Add a New Contact:add_contact:perl
e:Display Contact Information:lookup_contact:perl
f:Display All Contacts:print_contacts:perl
</font>

Menu options

<font face="Courier">d</font>
,
<font face="Courier">e</font>
and
<font face="Courier">f</font>
display additional menu options, but when they are selected, they will call Perl functions that are internal to the program. The new menu screen is shown below.

<font face="Courier">        a       Say Hello Gracie
        b       Show Perl man pages
        c       Show Current Directory
        e       Add a New Contact
        f       Display Contact Information
        g       Display All Contacts
        x       Exit


Enter your selection
</font>

The actual change to the menu is very simple and is show in the following listing. An explanation of the changes follows the listing, as promised.

<font face="Courier">#!/usr/bin/perl

#---------------------------------------
# MAIN ROUTINE
#---------------------------------------
# Display a menu and get a selection
get_menu_pick();

# as long as the E(x)it option is not chosen,
# execute the menu option and then display
# the menu again and ask for another choice

while ( $pick ne "x" )
{
	do_pick();
	get_menu_pick();
}

# clear the screen and exit with a 0 return code
clear_screen();

exit (0);

#---------------------------------------
# MAIN ROUTINE ENDS
#---------------------------------------

# Clear the screen, Show the menu and get user input
sub get_menu_pick
{
	clear_screen();
	show_menu();
	get_pick();
}

# Clear the screen by printing 25 newlines
sub clear_screen
{
	for ($i=0; $i < 25; ++$i){
		print "\n";
	}
}

# Open menufile.txt or exit with an error
# read in each row picking up the first two fields by 
# splitting it on the pipe |
# print the first two fields
# send some formfeeds to do some centering
sub show_menu
{
	$count = 0;
	open( MENUFILE, "menufile.txt") or die "Can't open menufile.txt: $!\n";
	while ($menurow=<MENUFILE>)
	{
		($menupick,$menuprompt)=split /:/,$menurow;
		print "\t$menupick\t$menuprompt \n";
		++$count;
	}
	close MENUFILE;
	print "\tx\tExit\n";
	++$count;
	$count = (24 - $count ) / 2;
	for ($i=0; $i < $count; ++$i){
		print "\n";
	}
	print "\n\nEnter your selection\n";

}

# get user input and chop off the newline
sub get_pick()
{
	chomp($pick = <STDIN>);
}


# Do the pick the user requested either as a call to the system
# or as an internal perl function

sub do_pick()
{

	open( MENUFILE, "menufile.txt") or die "Can't open menufile.txt: $!\n";
	while ($menurow=<MENUFILE>)
	{
		($menupick,$menuprompt,$menucommand,$menutype)=split /:/,$menurow;
		if ($menupick eq $pick)
		{
			if ($menutype eq "system" )
				{
				system $menucommand;
				}
			else
				{
				&$menucommand;
				}

			break;
		}
	}
	close MENUFILE;
	press_enter();
}

# put up a message and wait for user to press ENTER
sub press_enter
{
	print "Press Enter to Continue . . .\n";
	$dummy = <STDIN>;
}
</font>

The major change in the menu routine is show below at lines 77 through 104 in the

<font face="Courier">do_pick()</font>
routine. At line 86 the row that has been read in from
<font face="Courier">menufile.txt</font>
is split into four fields instead of three. The fourth field includes the
<font face="Courier">$menutype</font>
. At lines 89 through 96, the
<font face="Courier">$menutype</font>
is tested, and if it is
<font face="Courier">"system"</font>
, then the command extracted in
<font face="Courier">$menucommand</font>
is executed via system. Other wise the command is executed as
<font face="Courier">&$menucommand</font>
. The ampersand is Perl's way of flagging a variable or identifier as the name of a function. Officially, the ampersand is part of the function name, but in most contexts, Perl can figure out that you want to call (or define/declare) a function, and the ampersand is optional. In this case, the content of
<font face="Courier">$menucommand</font>
has been read in from a file, and Perl needs the ampersand to recognize that it is supposed to call a function that is named by the value in
<font face="Courier">$menucommand</font>
.

<font face="Courier">77	# Do the pick the user requested either as a call to the system
78	# or as an internal perl function
79	
80	sub do_pick()
81	{
82	
83		open( MENUFILE, "menufile.txt") or die "Can't open menufile.txt: $!\n";
84		while ($menurow=<MENUFILE>)
85		{
86			($menupick,$menuprompt,$menucommand,$menutype)=split /:/,$menurow;
87			if ($menupick eq $pick)
88			{
89				if ($menutype eq "system" )
90					{
91					system $menucommand;
92					}
93				else
94					{
95					&$menucommand;
96					}
97				
98				break;
99			}
100		}
101		close MENUFILE;
102		press_enter();
103	}
104	
</font>

Now we have a method of calling a Perl function, and a method of putting those functions on a menu, but where are the functions? If you look back at the new version of

<font face="Courier">menufile.txt</font>
you will see that it is looking for Perl functions named
<font face="Courier">add_contact</font>
,
<font face="Courier">lookup_contact</font>
, and
<font face="Courier">print_contacts</font>
. These will be functions directly added into the Perl menu program. For now, add the following lines of code to the end of your existing project (or cut these lines and paste them to the end of the project). To test that the process of calling an internal Perl function is working correctly, run your Perl program by typing
<font face="Courier">Perl menu</font>
(or whatever name you have chosen for this project). Enter a
<font face="Courier">d</font>
, an
<font face="Courier">e</font>
, and an
<font face="Courier">f</font>
from the menu to ensure that you are getting the three messages.

<font face="Courier">sub add_contact
{
	print "Adding a contact. \n"
}

sub lookup_contact
{
	print "Looking up a contact. \n"
}

sub print_contacts
{
	print "Printing all contacts. \n"
}
</font>

The complete program as it is supposed to look is shown below. The explanation follows. Be warned that this program does not always contain the best way to get a particular job done; its purpose is to illustrate basic Perl programming constructs. I have also used some different styles for blocking (enclosing statements in braces) just for illustration.

RELATED TOPICS
1 2 3 Page
Top 10 Hot Internet of Things Startups
View Comments
You Might Like
Join the discussion
Be the first to comment on this article. Our Commenting Policies