old-www/LDP/abs/html/sha-bang.html

1062 lines
17 KiB
HTML

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML
><HEAD
><TITLE
>Starting Off With a Sha-Bang</TITLE
><META
NAME="GENERATOR"
CONTENT="Modular DocBook HTML Stylesheet Version 1.7"><LINK
REL="HOME"
TITLE="Advanced Bash-Scripting Guide"
HREF="index.html"><LINK
REL="UP"
TITLE="Introduction"
HREF="part1.html"><LINK
REL="PREVIOUS"
TITLE="Shell Programming!"
HREF="why-shell.html"><LINK
REL="NEXT"
TITLE="Invoking the script"
HREF="invoking.html"></HEAD
><BODY
CLASS="CHAPTER"
BGCOLOR="#FFFFFF"
TEXT="#000000"
LINK="#0000FF"
VLINK="#840084"
ALINK="#0000FF"
><DIV
CLASS="NAVHEADER"
><TABLE
SUMMARY="Header navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TH
COLSPAN="3"
ALIGN="center"
>Advanced Bash-Scripting Guide: </TH
></TR
><TR
><TD
WIDTH="10%"
ALIGN="left"
VALIGN="bottom"
><A
HREF="why-shell.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="80%"
ALIGN="center"
VALIGN="bottom"
></TD
><TD
WIDTH="10%"
ALIGN="right"
VALIGN="bottom"
><A
HREF="invoking.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
></TABLE
><HR
ALIGN="LEFT"
WIDTH="100%"></DIV
><DIV
CLASS="CHAPTER"
><H1
><A
NAME="SHA-BANG"
></A
>Chapter 2. Starting Off With a Sha-Bang</H1
><TABLE
BORDER="0"
WIDTH="100%"
CELLSPACING="0"
CELLPADDING="0"
CLASS="EPIGRAPH"
><TR
><TD
WIDTH="45%"
>&nbsp;</TD
><TD
WIDTH="45%"
ALIGN="LEFT"
VALIGN="TOP"
><I
><P
><I
>Shell programming is a 1950s juke box . . .</I
></P
><P
><I
>--Larry Wall</I
></P
></I
></TD
></TR
></TABLE
><DIV
CLASS="TOC"
><DL
><DT
><B
>Table of Contents</B
></DT
><DT
>2.1. <A
HREF="invoking.html"
>Invoking the script</A
></DT
><DT
>2.2. <A
HREF="prelimexer.html"
>Preliminary Exercises</A
></DT
></DL
></DIV
><P
>In the simplest case, a script is nothing more than a list
of system commands stored in a file. At the very least, this saves
the effort of retyping that particular sequence of commands each
time it is invoked.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="EX1"
></A
><P
><B
>Example 2-1. <I
CLASS="FIRSTTERM"
>cleanup</I
>: A script to clean up log
files in /var/log </B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
># Cleanup
# Run as root, of course.
cd /var/log
cat /dev/null &#62; messages
cat /dev/null &#62; wtmp
echo "Log files cleaned up."</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
>There is nothing unusual here, only a set of commands that
could just as easily have been invoked one by one from the
command-line on the console or in a terminal window.
The advantages of placing the commands in a script go far beyond
not having to retype them time and again. The script becomes a
<I
CLASS="FIRSTTERM"
>program</I
> -- a <EM
>tool</EM
> --
and it can easily be modified or customized for a particular
application.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="EX1A"
></A
><P
><B
>Example 2-2. <I
CLASS="FIRSTTERM"
>cleanup</I
>: An improved clean-up
script</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# Proper header for a Bash script.
# Cleanup, version 2
# Run as root, of course.
# Insert code here to print error message and exit if not root.
LOG_DIR=/var/log
# Variables are better than hard-coded values.
cd $LOG_DIR
cat /dev/null &#62; messages
cat /dev/null &#62; wtmp
echo "Logs cleaned up."
exit # The right and proper method of "exiting" from a script.
# A bare "exit" (no parameter) returns the exit status
#+ of the preceding command. </PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
>Now <EM
>that's</EM
> beginning to look like a real
script. But we can go even farther . . .</P
><DIV
CLASS="EXAMPLE"
><A
NAME="EX2"
></A
><P
><B
>Example 2-3. <I
CLASS="FIRSTTERM"
>cleanup</I
>: An enhanced
and generalized version of above scripts.</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# Cleanup, version 3
# Warning:
# -------
# This script uses quite a number of features that will be explained
#+ later on.
# By the time you've finished the first half of the book,
#+ there should be nothing mysterious about it.
LOG_DIR=/var/log
ROOT_UID=0 # Only users with $UID 0 have root privileges.
LINES=50 # Default number of lines saved.
E_XCD=86 # Can't change directory?
E_NOTROOT=87 # Non-root exit error.
# Run as root, of course.
if [ "$UID" -ne "$ROOT_UID" ]
then
echo "Must be root to run this script."
exit $E_NOTROOT
fi
if [ -n "$1" ]
# Test whether command-line argument is present (non-empty).
then
lines=$1
else
lines=$LINES # Default, if not specified on command-line.
fi
# Stephane Chazelas suggests the following,
#+ as a better way of checking command-line arguments,
#+ but this is still a bit advanced for this stage of the tutorial.
#
# E_WRONGARGS=85 # Non-numerical argument (bad argument format).
#
# case "$1" in
# "" ) lines=50;;
# *[!0-9]*) echo "Usage: `basename $0` lines-to-cleanup";
# exit $E_WRONGARGS;;
# * ) lines=$1;;
# esac
#
#* Skip ahead to "Loops" chapter to decipher all this.
cd $LOG_DIR
if [ `pwd` != "$LOG_DIR" ] # or if [ "$PWD" != "$LOG_DIR" ]
# Not in /var/log?
then
echo "Can't change to $LOG_DIR."
exit $E_XCD
fi # Doublecheck if in right directory before messing with log file.
# Far more efficient is:
#
# cd /var/log || {
# echo "Cannot change to necessary directory." &#62;&#38;2
# exit $E_XCD;
# }
tail -n $lines messages &#62; mesg.temp # Save last section of message log file.
mv mesg.temp messages # Rename it as system log file.
# cat /dev/null &#62; messages
#* No longer needed, as the above method is safer.
cat /dev/null &#62; wtmp # ': &#62; wtmp' and '&#62; wtmp' have the same effect.
echo "Log files cleaned up."
# Note that there are other log files in /var/log not affected
#+ by this script.
exit 0
# A zero return value from the script upon exit indicates success
#+ to the shell.</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
>Since you may not wish to wipe out the entire system log,
this version of the script keeps the last section of the message
log intact. You will constantly discover ways of fine-tuning
previously written scripts for increased effectiveness.</P
><P
><A
NAME="SHABANGREF"
></A
>* * *</P
><P
><A
NAME="MAGNUMREF"
></A
>The
<I
CLASS="FIRSTTERM"
> sha-bang</I
>
(<SPAN
CLASS="TOKEN"
> #!</SPAN
>)
<A
NAME="AEN205"
HREF="#FTN.AEN205"
><SPAN
CLASS="footnote"
>[1]</SPAN
></A
>
at the head of a script tells your system that this file is a set
of commands to be fed to the command interpreter indicated. The
<SPAN
CLASS="TOKEN"
>#!</SPAN
> is actually a two-byte
<A
NAME="AEN214"
HREF="#FTN.AEN214"
><SPAN
CLASS="footnote"
>[2]</SPAN
></A
>
<I
CLASS="FIRSTTERM"
>magic number</I
>, a special marker that
designates a file type, or in this case an executable shell
script (type <TT
CLASS="USERINPUT"
><B
>man magic</B
></TT
> for more
details on this fascinating topic). Immediately following
the <I
CLASS="FIRSTTERM"
>sha-bang</I
> is a <I
CLASS="FIRSTTERM"
>path
name</I
>. This is the path to the program that interprets
the commands in the script, whether it be a shell, a programming
language, or a utility. This command interpreter then executes
the commands in the script, starting at the top (the line
following the <I
CLASS="FIRSTTERM"
>sha-bang</I
> line), and ignoring
comments.
<A
NAME="AEN226"
HREF="#FTN.AEN226"
><SPAN
CLASS="footnote"
>[3]</SPAN
></A
>
</P
><P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/sh
#!/bin/bash
#!/usr/bin/perl
#!/usr/bin/tcl
#!/bin/sed -f
#!/bin/awk -f</PRE
></FONT
></TD
></TR
></TABLE
></P
><P
>Each of the above script header lines calls a different command
interpreter, be it <TT
CLASS="FILENAME"
>/bin/sh</TT
>, the default shell
(<B
CLASS="COMMAND"
>bash</B
> in a Linux system) or otherwise.
<A
NAME="AEN242"
HREF="#FTN.AEN242"
><SPAN
CLASS="footnote"
>[4]</SPAN
></A
>
Using <TT
CLASS="USERINPUT"
><B
>#!/bin/sh</B
></TT
>, the default Bourne shell
in most commercial variants of UNIX, makes the script <A
HREF="portabilityissues.html"
>portable</A
> to non-Linux machines,
though you <A
HREF="gotchas.html#BINSH"
>sacrifice Bash-specific
features</A
>. The script will, however, conform to the
<SPAN
CLASS="ACRONYM"
>POSIX</SPAN
>
<A
NAME="AEN256"
HREF="#FTN.AEN256"
><SPAN
CLASS="footnote"
>[5]</SPAN
></A
>
<B
CLASS="COMMAND"
>sh</B
> standard.</P
><P
>Note that the path given at the <SPAN
CLASS="QUOTE"
>"sha-bang"</SPAN
> must
be correct, otherwise an error message -- usually <SPAN
CLASS="QUOTE"
>"Command
not found."</SPAN
> -- will be the only result of running the
script.
<A
NAME="AEN269"
HREF="#FTN.AEN269"
><SPAN
CLASS="footnote"
>[6]</SPAN
></A
>
</P
><P
><SPAN
CLASS="TOKEN"
>#!</SPAN
> can be omitted if the script consists only
of a set of generic system commands, using no internal
shell directives. The second example, above, requires the
initial <SPAN
CLASS="TOKEN"
>#!</SPAN
>, since the variable assignment line,
<TT
CLASS="USERINPUT"
><B
>lines=50</B
></TT
>, uses a shell-specific construct.
<A
NAME="AEN279"
HREF="#FTN.AEN279"
><SPAN
CLASS="footnote"
>[7]</SPAN
></A
>
Note again that <TT
CLASS="USERINPUT"
><B
>#!/bin/sh</B
></TT
> invokes the default
shell interpreter, which defaults to <TT
CLASS="FILENAME"
>/bin/bash</TT
>
on a Linux machine.</P
><DIV
CLASS="TIP"
><P
></P
><TABLE
CLASS="TIP"
WIDTH="100%"
BORDER="0"
><TR
><TD
WIDTH="25"
ALIGN="CENTER"
VALIGN="TOP"
><IMG
SRC="../images/tip.gif"
HSPACE="5"
ALT="Tip"></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
><P
>This tutorial encourages a modular approach
to constructing a script. Make note of and collect
<SPAN
CLASS="QUOTE"
>"boilerplate"</SPAN
> code snippets that might be useful
in future scripts. Eventually you will build quite an extensive
library of nifty routines. As an example, the following script
prolog tests whether the script has been invoked with the correct
number of parameters.</P
><P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>E_WRONG_ARGS=85
script_parameters="-a -h -m -z"
# -a = all, -h = help, etc.
if [ $# -ne $Number_of_expected_args ]
then
echo "Usage: `basename $0` $script_parameters"
# `basename $0` is the script's filename.
exit $E_WRONG_ARGS
fi</PRE
></FONT
></TD
></TR
></TABLE
>
</P
><P
>Many times, you will write a script that carries out one
particular task. The first script in this chapter is an
example. Later, it might occur to you to generalize
the script to do other, similar tasks. Replacing the literal
(<SPAN
CLASS="QUOTE"
>"hard-wired"</SPAN
>) constants by variables is a step in
that direction, as is replacing repetitive code blocks by <A
HREF="functions.html#FUNCTIONREF"
>functions</A
>.</P
></TD
></TR
></TABLE
></DIV
></DIV
><H3
CLASS="FOOTNOTES"
>Notes</H3
><TABLE
BORDER="0"
CLASS="FOOTNOTES"
WIDTH="100%"
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="5%"
><A
NAME="FTN.AEN205"
HREF="sha-bang.html#AEN205"
><SPAN
CLASS="footnote"
>[1]</SPAN
></A
></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="95%"
><P
>More commonly seen in the literature as
<I
CLASS="FIRSTTERM"
>she-bang</I
> or <I
CLASS="FIRSTTERM"
>sh-bang</I
>.
This derives from the concatenation of the tokens
<I
CLASS="FIRSTTERM"
>sharp</I
> (<SPAN
CLASS="TOKEN"
>#</SPAN
>) and
<I
CLASS="FIRSTTERM"
>bang</I
> (<SPAN
CLASS="TOKEN"
>!</SPAN
>).</P
></TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="5%"
><A
NAME="FTN.AEN214"
HREF="sha-bang.html#AEN214"
><SPAN
CLASS="footnote"
>[2]</SPAN
></A
></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="95%"
><P
>Some flavors of UNIX (those based on 4.2 BSD)
allegedly take a four-byte magic number, requiring
a blank after the <SPAN
CLASS="TOKEN"
>!</SPAN
> --
<TT
CLASS="USERINPUT"
><B
>#! /bin/sh</B
></TT
>. <A
HREF="http://www.in-ulm.de/~mascheck/various/shebang/#details"
TARGET="_top"
> According to Sven Mascheck</A
> this is probably a myth.</P
></TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="5%"
><A
NAME="FTN.AEN226"
HREF="sha-bang.html#AEN226"
><SPAN
CLASS="footnote"
>[3]</SPAN
></A
></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="95%"
><P
>The <SPAN
CLASS="TOKEN"
>#!</SPAN
> line in a shell script
will be the first thing the command interpreter
(<B
CLASS="COMMAND"
>sh</B
> or <B
CLASS="COMMAND"
>bash</B
>)
sees. Since this line begins with a <SPAN
CLASS="TOKEN"
>#</SPAN
>,
it will be correctly interpreted as a comment when the
command interpreter finally executes the script. The
line has already served its purpose - calling the command
interpreter.</P
><P
>If, in fact, the script includes an
<EM
>extra</EM
> <SPAN
CLASS="TOKEN"
>#!</SPAN
> line, then
<B
CLASS="COMMAND"
>bash</B
> will interpret it as a comment.
<TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
echo "Part 1 of script."
a=1
#!/bin/bash
# This does *not* launch a new script.
echo "Part 2 of script."
echo $a # Value of $a stays at 1.</PRE
></FONT
></TD
></TR
></TABLE
></P
></TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="5%"
><A
NAME="FTN.AEN242"
HREF="sha-bang.html#AEN242"
><SPAN
CLASS="footnote"
>[4]</SPAN
></A
></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="95%"
><P
>This allows some cute tricks.</P
><P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/rm
# Self-deleting script.
# Nothing much seems to happen when you run this... except that the file disappears.
WHATEVER=85
echo "This line will never print (betcha!)."
exit $WHATEVER # Doesn't matter. The script will not exit here.
# Try an echo $? after script termination.
# You'll get a 0, not a 85.</PRE
></FONT
></TD
></TR
></TABLE
></P
><P
>Also, try starting a <TT
CLASS="FILENAME"
>README</TT
> file with a
<TT
CLASS="USERINPUT"
><B
>#!/bin/more</B
></TT
>, and making it executable.
The result is a self-listing documentation file. (A <A
HREF="here-docs.html#HEREDOCREF"
>here document</A
> using
<A
HREF="basic.html#CATREF"
>cat</A
> is possibly a better alternative
-- see <A
HREF="here-docs.html#EX71"
>Example 19-3</A
>).</P
></TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="5%"
><A
NAME="FTN.AEN256"
HREF="sha-bang.html#AEN256"
><SPAN
CLASS="footnote"
>[5]</SPAN
></A
></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="95%"
><P
><A
NAME="POSIX2REF"
></A
><STRONG
>P</STRONG
>ortable
<STRONG
>O</STRONG
>perating
<STRONG
>S</STRONG
>ystem <EM
>I</EM
>nterface, an attempt to
standardize UNI<STRONG
>X</STRONG
>-like
OSes. The POSIX specifications are listed on the <A
HREF="http://www.opengroup.org/onlinepubs/007904975/toc.htm"
TARGET="_top"
>Open
Group site</A
>.</P
></TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="5%"
><A
NAME="FTN.AEN269"
HREF="sha-bang.html#AEN269"
><SPAN
CLASS="footnote"
>[6]</SPAN
></A
></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="95%"
><P
>To avoid this possibility, a script may begin
with a <A
HREF="system.html#ENVV2REF"
>#!/bin/env bash</A
>
<I
CLASS="FIRSTTERM"
>sha-bang</I
> line. This may be
useful on UNIX machines where <I
CLASS="FIRSTTERM"
>bash</I
>
is not located in <TT
CLASS="FILENAME"
>/bin</TT
></P
></TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="5%"
><A
NAME="FTN.AEN279"
HREF="sha-bang.html#AEN279"
><SPAN
CLASS="footnote"
>[7]</SPAN
></A
></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="95%"
><P
>If <I
CLASS="FIRSTTERM"
>Bash</I
> is your default
shell, then the <SPAN
CLASS="TOKEN"
>#!</SPAN
> isn't necessary at the
beginning of a script. However, if launching a script from
a different shell, such as <I
CLASS="FIRSTTERM"
>tcsh</I
>,
then you <EM
>will</EM
> need the
<SPAN
CLASS="TOKEN"
>#!</SPAN
>.</P
></TD
></TR
></TABLE
><DIV
CLASS="NAVFOOTER"
><HR
ALIGN="LEFT"
WIDTH="100%"><TABLE
SUMMARY="Footer navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
><A
HREF="why-shell.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="index.html"
ACCESSKEY="H"
>Home</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
><A
HREF="invoking.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
>Shell Programming!</TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="part1.html"
ACCESSKEY="U"
>Up</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
>Invoking the script</TD
></TR
></TABLE
></DIV
></BODY
></HTML
>