NOTE: I felt the need to start a meditation on this topic to collect ideas from the community, because:

A) I see a strong need for specific guidelines on how to write Perl for portability, as an alternative to hoping that "Perl will just do the right thing" (which ain't always so), and

B) Although I've been writing Perl programs for Unix (Linux, etc.) systems for nearly a decade, my non-Unix experience with Perl is very limited--so I'm eager to tap into the Wisdom of the Monastery for the benefit of improving this Meditation to the point where it becomes a Tutorial.

Patches welcome! 8-}

A Primer on Writing Portable
Perl Programs

Tim Maher, Consultix

tim@TeachMePerl.com

Perl is rightfully famous for being Operating System (OS) “portable”.
This means that the Perl language itself can run on a wide variety of OSs, which is certainly a nice feature. But more importantly, it also means that Perl
programs written with OS-independence in mind can generally be transported from
one OS to another, and successfully run there, without any changes—and that's a
fantastic feature.

In this tutorial, you'll learn the basic principles for
converting Perl scripts and one-liner commands written for Unix1 systems into forms usable on other systems.
Although the general portability issues we'll discuss apply to all OSs, we'll concentrate on Windows as our example of a non-Unix OS, because of its widespread
availability.

On Windows, individual Perl scripts can be run by clicking their
associated icons on the graphical display, but Perl one-liners need to be
submitted to the Windows “shell” (cmd.exe). For
simplicity, we'll restrict our focus to this shell environment for running both
scripts and commands. It's accessed by clicking on Start, followed by Run,
and then typing cmd, which causes a
"DOS"-style terminal window to appear, with a typical prompt of C:\.

Specific instructions for running Perl programs on systems other
than Unix and Windows (VMS, Mac, etc.) may be found in the reference documents
cited in section 3, “Additional resources”.

Next we'll discuss the major techniques used to make programs OS
portable.

1. Programming for OS independence

In order to reap the benefits of Perl's capacity for OS portability,
you must avoid writing code that depends on OS-specific resources or
conventions. We'll consider the two most important cases here, having to do
with OS-specific commands
and pathnames. Additional
problem areas are discussed in the resources listed in section 3.

1.1 Avoiding OS-specific commands

Although Perl is a very clever language, it's understandably
incapable of executing a Unix-specific statement like this one on non-Unix OSs:

What's non-portable about this statement? It requires its host
OS2 to have Unix-like who and grep commands, a LOGNAME environment variable, the >
redirection symbol, and the /dev/null special
device—not to mention command exit codes whose True/False values are opposite
to Perl's (which accounts for the !).

But the above example is a rather extreme case; in practice, most
of the statements in an average Perl program would likely run on other OSs without change, and many of the remainder could easily be rewritten for OS portability.

Consider this call to system, which
is OS specific indeed, yet still amenable to rehabilitation:3

There are two major strategies for avoiding OS-specific
statements like this one in order to make your code OS portable. The first is
to use OS-independent resources, such as features built-in to Perl or available
from modules, in preference to OS-specific ones.

Considering the above example from this perspective, Perl's
built-in localtime function could be used to
generate a string that's very similar to date's
output. The only difference is that it lacks the timezone information, which
isn't an issue because it's being discarded anyway. In addition, all of sed's functionality (and more) can be obtained from the
use of Perl's built-in substitution operator. These observations allow us to
replace the original Unix pipeline with this native Perl code:

This produces output identical to that of the date|sed pipeline, but in an OS-portable manner. As an added
bonus, the elimination of the Shell-level command pipeline obviates the need
for handling its exit code in an OS-independent manner.

The second major strategy for writing OS-independent code is one
you should strive to avoid
using. This technique involves writing separate chunks of OS-specific code in different
branches to handle the OS differences, using Perl's special host-OS-reporting variable,
$^O, to select the
appropriate branch for execution.

For example, the following branches use OS-specific commands to
display a long-listing of the file named in the first argument:

if ($^O eq 'MSWin32') { system "dir
$ARGV[0]";}elsif ($^O =~ /ix$/) { # matches our Unix-like OSs: AIX & IRIX system "ls -l
$ARGV[0]";else { warn "$0: WARNING:
Program might not work on '$^O'\n";}
Techniques based on use of the built-in stat
function could provide an OS-portable alternative to the use of these OS-specific
commands.
But even more conveniently, you could probably find an OS-portable
CPAN module that has already solved this problem, and use its resources instead
of your own (see http://search.cpan.org).

Another major source of portability problems is programmers
making certain kinds of assumptions about other OSs that may be invalid.
We'll see how to avoid making unnecessary assumptions about file-system related
differences next.

1.2 Avoiding OS-specific pathnames

There are contexts where Perl expects to find pathnames, such as
within @ARGV in programs using the n or p options, and as
arguments to the stat function. In such contexts,
Perl automatically converts slash separators in pathnames into backslashes—if
that's appropriate for the host OS. This means you don't have to code separate
branches of execution for different OSs (as shown earlier) just to handle that
chore.

For cases where Perl can't know in advance that a pathname will
be present, such as within the argument for the system
function, it's your responsibility to arrange for the slash-to-backslash
conversion—along with any other OS-required changes.

For programmer convenience, Perl provides a standard module to
help you perform OS-specific pathname conversions, called File::Spec::Functions.

You also must avoid making unfounded assumptions about other OSs, such as whether a particular directory (e.g., /tmp) will
necessarily exist there. File::Spec::Functions
helps with this task too, by providing a tmpdir function
that returns the name of the counterpart for /tmp
on the host OS.

The additional resources cited in section 3 discuss many other
important portability issues, along with specific recommendations for dealing
with them.

Having covered some important theoretical concerns in
"programming for OS portability", we'll now discuss some specific
recommendations for making Unix-bred Perl programs portable to Windows systems.

2. Running Perl programs on Windows

2.1 Running Perl one-liners on Windows

Many programmers find that the use of Perl one-liners increases
their productivity greatly, and Windows users are no exception. However, the
vast majority of the one-liners shown in most Perl books will not work if typed, for example, to a
Windows cmd.exe shell. That's because the single
quotes they use to convey the program code to the perl
command are not recognized as quoting characters by that shell.

There are two ways to address this problem:

·
convert the quoting techniques used in the one-liner for
compatibility with the target OS's shell5

·
convert the one-liner to a script that runs on the target OS

The first solution requires modifying the command's quoting in
an OS-dependent manner, while the second avoids the code-quoting issue
altogether by enclosing the program code in a file, which will only be read by
Perl.

Let's look at a specific example of reworking a command's quoting
for compatibility with Windows 2000 or XP (which share the same shell). Consider
the following one-liner:

perl -wl -e 'print "Crikey, what a little beauty!";'

This command won't run properly on the Windows systems mentioned;
here's the error message from Perl:

Can't find string terminator "'" anywhere before EOF at
-e line 1.

That message indicates that Perl did not receive the complete
program as the argument for -e, which is a
by-product of Windows not treating single quotes as quoting characters.

In trivial cases like this one, where either type of quotes will
work around the Perl string, simply swapping the internal double quotes with
the external single quotes can fix the problem. That's because double quotes,
unlike single quotes, are recognized as quoting characters by Windows,
permitting this reworked command to work as intended:

perl -wl -e "print 'Crikey, what a little beauty!';"

In cases where double quotes must be used within the Perl
program itself, backslashing allows them to coexist with the shell-level outer
double quotes:

perl -wl -e "print \"The arguments are:
@ARGV\";"

Next will discuss techniques for making scripts more portable.

2.2 Running Perl scripts on Windows

Assuming a script has been written with OS portability in mind
as described above, it needn't take much work to get it to run on a non-Unix
system.

For instance, on a Windows machine that has a working Perl
installation,6
invoking a script as:

C>\ perl myscript

should be sufficient to run it (assuming the shell is properly
configured to know how to find perl).

But the more typical approach is to add a Perl-specific file
extension to each Perl script, to allow Windows to invoke perl
on it automatically when you type its name to the shell prompt:

C>\ myscript.pl

The association between the .pl
extension and Perl (or the .plx
extension) is generally created at the time Perl is installed, but if you need
to set that up yourself, the instructions are provided in perldoc
perlwin32.

When you run scripts using either technique shown above,
any invocation options provided on the script's Unix-oriented shebang line will
be recognized and put into effect by the perl
command. For this reason, you should leave your shebang lines in place when you
transfer scripts from Unix to other OSs, despite the fact that they won't be
used to locate the perl command itself (as they do
on Unix).

Although getting your scripts to execute on Windows should not be
a problem, obtaining the benefits of certain services provided by the Unix
shells, which are far more sophisticated than their Windows counterparts, may
not be so easy.

For example, let's say you wanted to supply filename arguments to
a script using “wildcard” characters, as in this Unix command:

$ myscript *.txt

Special techniques would have to be used to arrange for *.txt to be processed
properly, as detailed in perldoc perlwin32 (search
for “Wild.pm”).

3. Additional resources

For additional information on writing Perl programs with OS portability
in mind, and for running Perl commands on non-Unix OSs, you may wish to consult
these resources:7

perldoc perlport
# General portability
issues

perldoc perlwin32
# Windows-specific portability issues

perldoc perlos2
# OS2-specific portability
issues

perldoc perlmac
# Mac-specific portability
issues

perldoc perlvms
# VMS-specific portability
issues

perldoc perlmacosx
# Mac OS X-specific portability issues

perldoc File::Spec::Functions
# Useful portability functions

perldoc File::Spec
# Functions of File::Spec::Functions

perldoc File::Spec::Unix
# Unix-specific pathname info

perldoc File::Spec::Win32
# Windows-specific pathname info

perldoc File::Spec::Mac
# Mac-specific pathname information

perldoc File::Spec::OS2
# OS2-specific pathname information

perldoc File::Spec::VMS
# VMS-specific pathname information

perldoc perlrun
# Invocation options and shebang
lines

1
For our purposes, the term “Unix” refers to actual UNIX systems as well as
functionally similar OSs such as Linux and Mac OS/X’s FreeBSD.

3Because
the $ character within the double quotes seems to
be introducing a request for the interpolation of the variable $/, the $ needs to be
backslashed to be treated as a literal character by Perl.

4See
man perlport for the name strings that Perl uses
for other OSs, such as “MSWin32” for 32-bit Microsoft Windows systems.

6At
the time of this writing the Activestate corporation (see http://activestate.com) was still the
undisputed vendor of choice for high quality and freely available versions

7When
you're on a Unix system, you could also use the man
command to access these documents; however, on another OS, which you're likely
to be visiting when you refer to this page, perldoc
would be the appropriate command to use.

When putting a smiley right before a closing parenthesis, do you:

Use two parentheses: (Like this: :) )
Use one parenthesis: (Like this: :)
Reverse direction of the smiley: (Like this: (: )
Use angle/square brackets instead of parentheses
Use C-style commenting to set the smiley off from the closing parenthesis
Make the smiley a dunce: (:>
I disapprove of emoticons
Other