[oak perl] shell script or perl?

Belden Lyman blyman at iii.com
Thu Jun 24 17:21:29 CDT 2004


This may be interesting to someone else.

Today at work I developed a perl script that manipulates a text file in 
some useful way. Then I realized that not all of our servers have perl 
installed on them, so I re-developed the data manipulation in sed.

But this was a loss, because in perl the program did some extra 
reporting which I couldn't make sed do- a verbose mode and the like. I
know extra reporting will be useful, so decided to compromise: combine 
the two programs together into a single executable script.

If perl is found, then the perl portion of the script gets run. If 
not, then the shell script portion picks out what arguments it knows 
about, warns that it is ignoring other arguments, and runs the sed 
command that does the data transformation.

I've hoisted out the guts of the script because the text manipulation
isn't all that interesting. However, the bit that selects between using
shell or perl is kind of neat:

     1  #!/bin/ksh
     2  `perl -e 1 > /dev/null 2>&1` && eval 'exec perl -x $0 $*'
     3  
     4  echo "hello shell script"
     5  for opt in $*
     6  do
     7    echo "shell: $opt"
     8  done
     9  exit 0
    10  
    11  #!perl -l
    12  print "hello perl script";
    13  print "perl: $_" for @ARGV;
    14  exit 1;
    15  __END__

All the shell scripting falls between lines 3 and 10. The perl
code takes up lines 11-15.

These are the important lines in the script:

     2  `perl -e 1 > /dev/null 2>&1` && eval 'exec perl -x $0 $*'
     
    11  #!perl -l

    15  __END__

Line 2 is executed by the shell, /bin/ksh. It starts with

     `perl -e 1 > /dev/null 2>&1`

which tries to invoke the perl interpreter to execute the command
statement '1;'. If perl can be found on the system, then the
interpreter starts up, does nothing, and exits with a true value.

If perl cannot be found, then the shell started by my `backticks` spits 
out an error message, which gets redirected to /dev/null. The `backtick`
shell exits with a false value.

The '&&' on line 2 causes the right side, "eval 'exec perl -x $0 $*'", 
to be executed only if the left side returns a true value. So, where
a perl interpreter is available, the kernel replaces the running shell
script with the following process:

    perl -x script_name_here script_args_here

The -x switch to perl is pretty neat: 'perldoc perlrun' says that it

      tells Perl that the program is embedded in a larger chunk of
      unrelated ASCII text, such as in a mail message.

(Well, I'm embedding it in a shell script, but same difference.)

      Leading garbage will be discarded until the first line that 
      starts with #! and contains the string "perl".

(Hence line 11 being a very important line!)

      Any meaningful switches on that line will be applied.

(I tossed on a -l switch so the prints have a newline automatically
appended.)

      If a directory name is specified, Perl will switch to that
      directory before running the program.

(I didn't bother with this, though I suppose changing to /tmp might
not be a bad idea: both the shell script and the perl script read
input from a pipe, so a chdir wouldn't hurt anything.)

      The -x switch controls only the disposal of leading garbage.
      The program must be terminated with "__END__"

(And that's why line 15 is another important line.)

Tada, a script written in shell and perl!

Belden

ps-

You can simulate running the script on a server where perl isn't found
by changing line 2 from this:

     2  `perl -e 1 > /dev/null 2>&1` && eval 'exec perl -x $0 $*'

to this:

     2  `$1 -e 1 > /dev/null 2>&1` && eval 'exec perl -x $0 $*'

and then you can run:

     $ ksh-or-perl fnurffanurf hello world
     hello shell script
     shell: fnurffanurf
     shell: hello
     shell: world

and on a server where perl is found:

     $ ksh-or-perl perl hello world
     perl: perl
     perl: hello
     perl: world

pps- if I needed to put any shell script after the perl code, I'd 
change

     9  exit 0

to

     9  cat<<__END__>/dev/null

which allows the shell script to see the perl script as a here-doc
that gets catted to /dev/null.





More information about the Oakland mailing list