Chapter 4. Interacting with the Environment

No shell script is an island. Your scripts run within the environment provided by your computer and operating system. For shell scripts, most systems provide a Unix-like environment, which helps a lot because your scripts can query the environment in a consistent manner across systems. This includes Mac OS X, based on Berkeley Unix, and Linux, a Unix work-alike. Even Windows and QNX provide Unix-like environments for shell scripts.

A Unix-like environment provides special settings called environment variables, which hold important values for determining where commands are located, as well as the user's home directory.

This chapter covers:

  • Examining the settings in your system's environment, getting and setting environment variables

  • Customizing your account, especially how shells start up

  • Handling command-line arguments and using these arguments to change the way your scripts behave

  • Making scripts executable so your scripts appear to the end user as operating system commands, no different from commands such as ls and cp

Examining Environment Variables

Environment variables are a type of shell variables.

For the most part, environment variables are the same as other shell variables. There are four main differences, however:

  • Environment variables are set by the environment, the operating system, during the startup of your shell. (This process is not magic, as you will see in this chapter.)

  • Shell variables are local to a particular instance of the shell—for example, the shell instance running a script. Environment variables are inherited by any program you start, including another shell.

  • Environment variables have a special meaning that applies to all scripts.

  • You must make a special operation to set an environment variable.

The following sections cover these points in depth.

Reading Environment Variables

Environment variables are treated, in most respects, as normal shell variables. That means that if you have an environment variable named HOME, you can access the value of this variable by using the dollar sign ($) constructor—for example, $HOME. This part is no different than for other variables that you create within your shell scripts.

The most crucial thing about these variables, however, is that they hold settings that presumably reflect the user's environment and wishes. (Sometimes these variables reflect the administrator's wishes instead.)

For example, the LANG environment variable, if set, specifies the user's locale, a combination of a code for the user's language, as well as any variants. A user could have a locale of English with a code of en. A user in the United Kingdom would have a variant of English in the UK, en_UK, whereas a user in the United States would have a variant of English in the US, en_US. A spell-checking program can then use the LANG environment variable to determine which spelling dictionary to load and thus determine whether specialization or specialisation is correct, for example.

Thus, your scripts should honor environment variable settings where possible. This is complicated, however, by the fact that not all systems set all variables. Thus, your scripts always have to check whether a variable is set first and then display a default value if necessary.

The following table lists the most commonly available environment variables.

Variable

Usage

COLUMNS

Number of text characters that can fit across one line in the shell window

DISPLAY

Names the graphics display for systems using the X Window System

HISTSIZE

Number of commands to store in the command history (bash and ksh)

HOME

User's home directory

HOSTNAME

Computer's host name

LANG

Specifies user's locale, such as French in France, French in Canada, French in Switzerland

LINES

Number of text lines that can fit in the shell window

PATH

List of directories searched for commands

PWD

Current directory

SHELL

Default shell

TERM

Ancient terminal type, if applicable

USER

User name

To see what is defined within your shell, use the set command.

Use setenv instead with csh and tcsh. See below for more on this.

Built into the shell, the set command lists all the variables currently set. If you have access systems running different operating systems, compare the different environments. The following examples show common settings on different operating systems.

Note that set shows more than just the true environment variables. This is explained in more detail later in this chapter.

The online documentation for each program that uses an environment variable should describe what variables are used as well as how the variables are used. For example, the command to view online documentation, man, uses the MANPATH environment variable, if set, to determine which directories to look for online manuals.

Handling Local and Environment Variables

Technically, the set command, used for the previous examples, lists all the variables that have a value. This includes environment variables as well as any other variables, called local variables. Local variables are variables available within the current shell. Environment variables are variables that have been exported. Exported variables are available for subshells. And a subshell is a shell launched from within a shell. Confused yet?

The concept of subshells introduces a hierarchy of shells, meaning that your scripts may operate within a subsubshell or a subsubsubshell, and so on. It works like this: When you log in, the system runs an application, typically a shell. This is the ancestor of all further shells you run. This ancestor shell is considered a login shell, a shell that gets executed for a user login. The ancestor shell sets up the environment. The ancestor shell then launches a number of applications. For example, in Linux with a graphical desktop, the login shell runs the X Window System server process (traditionally called X). The X server process then launches a number of graphical applications, including any shell windows that appear when you log in. Each of these shell windows is a graphical application. Each of these shell windows, in turn, runs shells. These shells are subshells.

You can then run shells from within these subshells. You've been doing that for each example so far. Every time you use commands such as sh, bash, csh, and so on, you are launching a subshell, or more technically, a subshell of the current subshell. (This is one reason why Unix and Linux systems seem to run so many processes.)

So when you create an environment variable, your script is setting up the value for subshells, children of the shell running your script. Thus, this is really useful when your scripts launch child scripts or programs and want to pass values to the child scripts or programs through environment variables. Your scripts, however, do not set environment variables in the ancestor shell; environment-variable values are exported downward, not upward.

In most cases, you will set up the environment for shells when each shell starts. See the section Customizing Your Account later in this chapter for more on editing the files checked when shells start.

This may seem confusing, but if you use the following guidelines, the situation should make more sense:

  • Set up the environment for your user account using the standard files defined for that purpose and described in the section Customizing Your Account.

  • Otherwise, set environment variables within shell scripts only if your scripts are calling scripts or programs and you need to modify the environment for the scripts or programs.

  • In all other cases, just read environment variables inside your scripts.

Listing the Environment with the C Shell

The T C shell does support a set command, but it does not output the full environment. For example:

$ set
COLORS  /etc/DIR_COLORS.xterm
_       !! | sort

addsuffix
argv    ()
cwd     /home2/ericfj
dirstack        /home2/ericfj
dspmbyte        euc
echo_style      both
edit
file    /home2/ericfj/.i18n
gid     500
group   ericfj
history 100
home    /home2/ericfj
killring        30
owd
path    (/usr/kerberos/bin /usr/local/mozilla /bin /usr/bin /usr/local/bin
/usr/X11R6/bin /home2/ericfj/bin /usr/java/j2sdk1.4.1_03/bin /home2/ericfj/eclipse
/home2/ericfj/apache-ant-1.5.4/bin)
prompt  [%n@%m %c]$
prompt2 %R?
prompt3 CORRECT>%R (y|n|e|a)?
shell   /bin/tcsh
shlvl   2
sourced 1
status  0
tcsh    6.12.00
term    xterm
tty     pts/19
uid     500
user    ericfj
version tcsh 6.12.00 (Astron) 2002-07-23 (i386-intel-linux) options 8b,nls,dl,al,kan,rh,color,dspm,filec

The set command shows internal settings of the C shell, not a list of environment variables. The C shell equivalent of the Bourne shell set command is setenv. When you run setenv from a T C shell on Linux, you will see output similar to the following:

$ setenv | sort
COLORTERM=gnome-terminal
CVSROOT=:pserver:ericfj@localhost:/home2/cvsrepos
DESKTOP_SESSION=default
DISPLAY=:0.0
G_BROKEN_FILENAMES=1
GDMSESSION=default
GNOME_DESKTOP_SESSION_ID=Default
GNOME_KEYRING_SOCKET=/tmp/keyring-w8mvQR/socket
GROUP=ericfj
GTK_RC_FILES=/etc/gtk/gtkrc:/home2/ericfj/.gtkrc-1.2-gnome2
HOME=/home2/ericfj
HOST=kirkwall
HOSTNAME=kirkwall
HOSTTYPE=i386-linux
INPUTRC=/etc/inputrc
JAVA_HOME=/usr/java/j2sdk1.4.1_03
KDEDIR=/usr
LANG=en_US.UTF-8
LESSOPEN=|/usr/bin/lesspipe.sh %s
LOGNAME=ericfj
LS_COLORS=no=00:fi=00:di=00;34:ln=00;36:pi=40;33:so=00;35:bd=40;33;01:cd=40;33;
01:or=01;05;37;41:mi=01;05;37;41:ex=00;32:*.cmd=00;32:*.exe=00;32:*.com=00;
32:*.btm=00;32:*.bat=00;32:*.sh=00;32:*.csh=00;32:*.tar=00;31:*.tgz=00;31:*.arj=00;
31:*.taz=00;31:*.lzh=00;31:*.zip=00;31:*.z=00;31:*.Z=00;31:*.gz=00;31:*.bz2=00;
31:*.bz=00;31:*.tz=00;31:*.rpm=00;31:*.cpio=00;31:*.jpg=00;35:*.gif=00;35:*.bmp=00;
35:*.xbm=00;35:*.xpm=00;35:*.png=00;35:*.tif=00;35:
MACHTYPE=i386
MAIL=/var/spool/mail/ericfj
OSTYPE=linux
PATH=/usr/kerberos/bin:/usr/local/mozilla:/bin:/usr/bin:/usr/local/bin:
/usr/X11R6/bin:/home2/ericfj/bin:/usr/java/j2sdk1.4.1_03/bin:/home2/ericfj/eclipse:
/home2/ericfj/apache-ant-1.5.4/bin
PWD=/home2/ericfj
QTDIR=/usr/lib/qt-3.3
SESSION_MANAGER=local/kirkwall:/tmp/.ICE-unix/27573
SHELL=/bin/tcsh
SHLVL=2
SSH_AGENT_PID=27574
SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass
SSH_AUTH_SOCK=/tmp/ssh-bxy27573/agent.27573
SUPPORTED=en_US.UTF-8:en_US:en
TERM=xterm
USER=ericfj
USERNAME=ericfj
VENDOR=intel
WINDOWID=23068746
XAUTHORITY=/home2/ericfj/.Xauthority

You can also run the printenv or env commands under the C shell to list the environment variables. For example:

$ printenv
COLORTERM=gnome-terminal
CVSROOT=:pserver:ericfj@localhost:/home2/cvsrepos
DESKTOP_SESSION=default
DISPLAY=:0.0
G_BROKEN_FILENAMES=1
GDMSESSION=default
GNOME_DESKTOP_SESSION_ID=Default
GNOME_KEYRING_SOCKET=/tmp/keyring-w8mvQR/socket
GROUP=ericfj
GTK_RC_FILES=/etc/gtk/gtkrc:/home2/ericfj/.gtkrc-1.2-gnome2
HOME=/home2/ericfj
HOST=kirkwall
HOSTNAME=kirkwall
HOSTTYPE=i386-linux
INPUTRC=/etc/inputrc
JAVA_HOME=/usr/java/j2sdk1.4.1_03
KDEDIR=/usr
LANG=en_US.UTF-8
LESSOPEN=|/usr/bin/lesspipe.sh %s
LOGNAME=ericfj
LS_COLORS=no=00:fi=00:di=00;34:ln=00;36:pi=40;33:so=00;35:bd=40;33;01:cd=40;33;
01:or=01;05;37;41:mi=01;05;37;41:ex=00;32:*.cmd=00;32:*.exe=00;32:*.com=00;
32:*.btm=00;32:*.bat=00;32:*.sh=00;32:*.csh=00;32:*.tar=00;31:*.tgz=00;31:*.arj=00;
31:*.taz=00;31:*.lzh=00;31:*.zip=00;31:*.z=00;31:*.Z=00;31:*.gz=00;31:*.bz2=00;
31:*.bz=00;31:*.tz=00;31:*.rpm=00;31:*.cpio=00;31:*.jpg=00;35:*.gif=00;35:*.bmp=00;
35:*.xbm=00;35:*.xpm=00;35:*.png=00;35:*.tif=00;35:
MACHTYPE=i386
MAIL=/var/spool/mail/ericfj
OSTYPE=linux
PATH=/usr/kerberos/bin:/usr/local/mozilla:/bin:/usr/bin:/usr/local/bin:
/usr/X11R6/bin:/home2/ericfj/bin:/usr/java/j2sdk1.4.1_03/bin:/home2/ericfj/eclipse:
/home2/ericfj/apache-ant-1.5.4/bin
PWD=/home2/ericfj
QTDIR=/usr/lib/qt-3.3
SESSION_MANAGER=local/kirkwall:/tmp/.ICE-unix/27573
SHELL=/bin/tcsh
SHLVL=2
SSH_AGENT_PID=27574
SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass
SSH_AUTH_SOCK=/tmp/ssh-bxy27573/agent.27573
SUPPORTED=en_US.UTF-8:en_US:en
TERM=xterm
USER=ericfj
USERNAME=ericfj
VENDOR=intel
WINDOWID=23068746
XAUTHORITY=/home2/ericfj/.Xauthority

Testing the Environment

As mentioned previously, your scripts should honor environment variable settings. But your scripts also need to handle the case where certain variables are not set. In most cases, you need a fallback strategy to handle such instances.

For example, the DISPLAY environment variable names the X Window System display, which represents a combination of monitor, keyboard, and mouse. Graphical X Window programs use the DISPLAY setting to know which X display to use. On a multiuser system, this can be very important.

Just about every Linux and Unix system uses the X Window System for graphics. Mac OS X systems can run X as add-on software. You can even run X under Cygwin on Windows.

If the DISPLAY environment variable has a value, then X Window programs should use that value. If the DISPLAY environment variable is not set, however, a program or script has three choices:

  • Use the default value for the DISPLAY, :0.0 in this case.

  • Assume the X Window System is not running.

  • Alert the user that the DISPLAY variable is not set, and the script can exit. This is called die a flaming death in shell scripting parlance.

There's no magic. Your shell scripts need to make similar decisions if crucial variables are not set. The following Try It Out shows some strategies for handling missing environment variables.

Setting Environment Variables

You can read environment variables just like other shell variables. You can also set environment variables just like other shell variables. But you must take extra steps to affix your new values into the environment.

Remember that this environment applies only to programs and shells your script launches. That is, the environment applies only to subshells. You need to modify the environment for shells by editing the files listed in the section Customizing Your Account so that the environment gets properly propagated to subshells.

There are two main reasons to set environment variables:

  • You want to customize your user account.

  • Your script needs to set an environment variable that is used by a command or script it calls.

To set an environment variable, you need to export it. To set a variable, you need the following syntax:

VARIABLE=VALUE

To export a variable into the environment of child processes—that is, to make a plain old shell variable into a super-duper environment variable—use the export command:

export VARIABLE

Thus, you will often see the following pattern used in shell startup scripts:

var=value
export var

You can combine the two commands onto one line, using the semicolon separator. For example:

var=value; export var

You can also use a shorthand version with just the export command:

export var=value

The export command may export more than one variable at a time. For example:

var1=value1
var2=value2
var3=value3
export var1 var2 var3

You will find all of these variants used in various shell scripts you encounter, especially in scripts that initialize shells.

In the bash shell, you can also use the set -a command to export all variables set in the shell:

$ set -a

Your scripts should not assume that a user has run this command, however.

You can see how the export command works, as well as how to create your own environment variables, by running the following example scripts.

Setting Environment Variables in the C Shell

The variable-setting examples so far have used the syntax for the Bourne shell, sh, supported by ksh and bash as well. As you'd expect, the C shell requires a different syntax, using the setenv built-in shell command. The setenv syntax follows:

setenv variable value

Note that there is no equal sign in this syntax.

For a good source of csh examples, see the system files /etc/csh.login and /etc/csh.cshrc, the system files for initializing the C shell (and tcsh, too), as covered in the section Customizing Your Account. For example:

setenv PATH "/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin"
setenv MAIL "/var/spool/mail/$USER"

You do not call export in csh or tcsh.

Reading Values into the Current Environment

When you run a script, your current shell, such as bash, launches a child shell, most often sh, to run the script. You can instead execute a script that modifies the current shell environment using the source command.

The basic syntax is:

source script_filename

The source command runs the given script in the context of the current shell, not in a child shell. This command is most often used to read in shell startup files to customize the shell.

In addition to the source command, you can use the dot (.) command:

.  script_filename

This has the same effect as the source command.

Customizing Your Account

When a shell starts up, it reads from a file or files to initialize settings for your system. These files are shell scripts. Some of these files, such as /etc/profile, reside in system directories. Most users cannot modify these files. Other files reside within your home directory. These files are available for you to modify as needed.

Any system files should normally be left alone, as they establish the proper defaults for your system. Administrators might customize these files to allow for sitewide settings.

The files in your home directory, however, are intended for you to modify as needed. You can add any commands you want, using the proper shell syntax, into your personal startup files.

Each shell has a set of files that it looks for, by name. Virtually all personal startup files are located in your home directory, and all but one start with a dot.

Remember that files with names that start with a dot are considered hidden files. Hidden files don't appear in file listings without special command-line options and are ignored by most shell globs, such as *.

The files must have exactly the right name, or the shell will ignore them. For example, for the bash shell, one of the files is .bashrc. If you want to use this file to initialize bash, you must name it .bashrc (with a leading dot), and the file must reside in your home directory. You must also have read permission for this file.

With a very long history, shells typically support more than one file to hold initial settings. Thus, you may be able to choose from a number of file names, such as .bash_profile or .bash_login, both of which serve the same purpose.

Note

Don't try to set environment variables unless you know how the variable is used. You may end up messing up applications on your system, applications that expect a particular value in an environment variable.

The following sections describe how each shell starts.

How the Bourne Shell Starts Up

If you run the Bourne shell as a login shell, it looks in your home directory for a file named .profile. The shell then sources the commands in this file to read any settings into the current environment.

The Bourne shell does this only if the shell is a login shell—that is, the shell run when you first log in. The Bourne shell does not read the .profile file when subsequent shells start.

The distinction between login shells and other shells is important to how all the shells start.

How the Korn Shell Starts Up

When you run ksh as a login shell, it looks for a file in your home directory named .profile and sources the commands in that file. This is the same as the Bourne shell.

If you run ksh as a non-login shell, the Korn shell looks for a startup file named by the ENV environment variable. This is similar to the C shell's .cshrc file.

If ENV names a directory, the Korn shell looks for a file named ksh_env within that directory. If ENV names a file, the Korn shell loads that file.

How the C Shell Starts Up

When you start csh as a login shell, it sources the commands in /etc/csh.cshrc and /etc/csh.login. Then csh looks for a file in your home directory named .cshrc and sources the commands in that file. After that, csh looks for a file in your home directory named .login and sources the commands in that file. On logout, a csh login shell sources the commands in /etc/csh.logout and the .logout file in your home directory.

For non-login shells, csh sources the commands in /etc/csh.cshrc and then .cshrc in your home directory.

How the T C Shell Starts Up

The T C shell starts up and acts similarly to the C shell. The main difference is that you can name the startup file .tcshrc or .cshrc. The T C shell will read either file. As with bash, the purpose for supporting the old .cshrc file is to make it easier for users to migrate from csh to tcsh.

How Bash Starts Up

Bash combines features of the C shell, the Bourne shell, and the Korn shell. Thus, you will see a number of files that appear similar to the files used by ksh or csh.

When you run bash as a login shell, it first executes the /etc/profile file. Then bash looks for files in your home directory. Bash looks for:

  • .bash_profile

  • .bash_login

  • .profile

Bash looks in this order and executes the first file found. The intent is to make it easier to switch from ksh or csh to bash.

When a login shell exits, bash looks for a file named .bash_logout in your home directory and sources the commands in the file.

You can launch bash as a login shell using the --login option. For example:

$ bash --login

Use the --noprofile option to tell a bash login shell not to read the login files on startup.

When you run bash as a child shell and not as a login shell, bash looks in one of two locations. If the shell is interactive, bash looks for a file named .bashrc in your home directory. Otherwise, bash checks for an environment variable named BASH_ENV. The BASH_ENV variable serves the same purpose and usage as the ksh ENV environment variable.

Use the --norc to tell bash to not run the .bashrc file. Use the --rcfile option to tell a noninteractive, non-login bash to use a different file name than .bashrc in your home directory.

The following table summarizes the startup, login, and logout files for the main shells.

Shell

Startup File

Login File

Logout File

bash

.bashrc (if not login shell but interactive), $BASH_ENV (if noninteractive and not login)

/etc/profile, then .bash_profile, or .bash_login, or .profile

.bash_logout

csh

/etc/csh.cshrc, then .cshrc

/etc/csh.cshrc, then /etc/csh.login, then .cshrc, then .login

/etc/csh.logout, then .logout

ksh

$ENV

.profile

None

sh

None

/etc/profile, then .profile

None

tcsh

/etc/csh.cshrc, and .tcshrc or .cshrc

/etc/csh.cshrc, then /etc/csh.login, then .tcshrc or .cshrc, then .login

/etc/csh.logout, then .logout

Note

Don't depend on shell customizations. While it is great that you can customize your environment, you should not write your shell scripts to depend on these customizations. That's because once you move to another system, your scripts may fail in unexpected ways.

Because the initialization of the shells is performed while the shell starts up, it is not always apparent what is causing a particular problem. You may spend hours trying to figure out what is wrong in your shell script when the problem lies elsewhere: in the shell initialization files.

Handling Command-Line Arguments

Another area where scripts interact with their environment is the command line. Just as you can pass command-line options and arguments to commands, you can also pass these to shell scripts. Of course, passing items on the command line is the easy part. The hard part comes in what do you do with them inside your scripts.

The first step is to examine all the items passed on the command line to your script.

Reading Command-Line Arguments with the Bourne Shell

When the Bourne shell runs a script, it places the command-line arguments in special variables that you can access within your script. For example, the variable $1 holds the first item on the command line for your script. This can be a command-line option, such as -v, or a command-line argument, such as the name of a file. The item in the first position is stored in $1.

That's why $1 and the like are called positional variables.

The following table lists the special variables set by the Bourne shell for the command-line arguments.

Special Variable

Holds

$0

Name of the script from the command line

$1

First command-line argument

$2

Second command-line argument

$3

Third command-line argument

$4

Fourth command-line argument

$5

Fifth command-line argument

$6

Sixth command-line argument

$7

Seventh command-line argument

$8

Eighth command-line argument

$9

Ninth command-line argument

$#

Number of command-line arguments

$*

All command-line arguments, separated with spaces

The script itself is given the special variable $0 (zero). The shell sets $0 with the name of the script, as it appears on the command line.

The command-line arguments are split out into separate variables, $1 to $9. There is no $10 and above. You are not limited to only nine arguments, however. The special variable $* holds all the arguments. In addition, $# holds a count of the number of arguments.

The Korn shell understands ${10} for the tenth argument, and so on.

Note that $0 is not counted and not considered part of $*.

Command-line arguments can be especially hard on Windows and Mac OS X, where directories with spaces in their names are common, such as the Windows C:Program Files directory.

Thus far, the example script has just listed the command-line arguments. The next step is to actually make use of them, as in the following Try It Out.

Reading Command-Line Arguments with the C Shell

The C shell and T C shell both support special variables for command-line arguments, similar to sh, bash, and ksh. The main difference with csh and tcsh, however, is the use of $#argv in place of $#.

With csh and tcsh, the special variable $#argv holds a count of the number of command-line arguments.

Note that tcsh also supports $# as well as $#argv. Both variables hold the same value.

Making Scripts Executable

Up to now, all the scripts require you to run a shell, such as sh, and to pass sh the name of the script file to execute. This is very different from running normal commands, such as ls. With ls, you just type in the name of the command, ls. You do not need to pass a script file to ls to generate the output.

As mentioned previously, you can transform your scripts into full-blown executable commands. Users can just type in the name of the command, and they never need know that your new command is really a script.

To transform your script into an executable command, you need to:

  • Mark the script as executable by giving it execute permissions

  • Add a line to your script to tell the shell how to execute the script

The following sections describe how to perform these steps.

Marking Files Executable

Under Unix and Unix-like systems, all executable files, be they scripts, commands, or something else, must be marked as executable files. An executable file is one that has execute permission. You can change permissions with the chmod command.

Before changing permissions, however, you should check what permissions are already set. To do this, use the ls command with the -l (ell) option. For example:

$ ls -l myls3
-rw-rw-r--  1 ericfj engineering 124 Oct 12 22:39 myls3

Each rw- describes the permissions for the owner of the file (user ericfj in this example), the group the file is associated with (engineering in this example), and the final r-- describes the permissions available to the rest of the world (all other users). The r means read permission. The w means write permission. A dash means no permission.

So rw- means read and write permission, and r-- means read-only permission. There are no execute permissions on this file.

To add an execute permission, use chmod. For example:

$ chmod u+x myls3

The u+x argument is in a funny form used by chmod. The u means user—that is, permissions for the user or owner of the file. The + means add permission. The x means execute permission.

You can also use octal numbers for permissions, leading to permissions such as 0666. See the online manual for the chmod command for more details.

You can now verify that the script has an execute permission with the ls command:

$ ls -l myls3
-rwxrw-r--  1 ericfj engineering 124 Oct 12 22:39 myls3

You can now see that the owner of the file has rwx permissions, short for read, write, and execute. Our script file is now an executable command.

Note that if you are using csh or tcsh, you must execute the rehash command to rebuild the internal list of executables. For example:

$ rehash

After making the script file executable, the next step is to tell your shell (as well as any other shell) how to execute the file.

Setting the #! Magic Line

Executable files—that is, commands—come from a variety of sources and appear in a variety of formats. Most commands are compiled executable programs. Most commands were written in the C programming language and then compiled into the binary code for your system's processor.

The main sources of commands, however, take one of the following formats.

  • Compiled executable programs

  • Java executable archives, called jar files

  • Scripts in languages such as Python, Perl, Tcl, Ruby, or Lua

  • Shell scripts

When you try to run an executable command, the first thing the shell must do is determine the type of file and then use the correct method to launch the command. For example, if the file is a compiled executable program, the first few bytes of the file will contain a special code (often called a magic code or magic number after the file /etc/magic, which holds many such codes). This special code tells the shell that it needs to launch the file as a compiled executable program.

The shell follows the same type of process for running shell scripts. First, the shell needs to determine that the file is a script. This process is helped by the fact that scripts are text files. The first few bytes (as well as all the bytes) of the file must be printable text characters.

Once the shell determines that the file is a script, the next question is what kind of script. The shell needs to determine what program should run the script. For example, if your shell is bash, but you write a Bourne shell script, then bash—your shell—needs to determine which program, sh in this case, to launch to run the script.

By convention, if your script starts with #! (often called a shebang because it starts with a hash-exclamation point), then the special comment tells the shell which program should interpret the script. So if the very first line of your script appears as the following, then the shell knows to launch sh to run your script:

#!/bin/sh

Note that this must be the first line of the file. The # must start on the first column of the file. That is, # must be the first character in the file. Because # starts a comment, if your shell, for some reason, doesn't understand this convention, the #! line will be ignored.

The syntax for the #! line is:

#!/full_path_to_interpreter

The interpreter is the program that interprets the script and executes the commands in the script. For Bourne shell scripts, the interpreter (or shell) is sh, with a full path of /bin/sh. For shells, the interpreter is the same as the shell program. But for some languages, such as Tcl, you have a choice of two interpreters: tclsh and wish, the latter supporting graphical commands.

Regardless of the interpreter, the shell merely takes the part after the #! and then tries to execute that program.

The following table lists the main locations for shells and interpreters.

Shell or Interpreter

Path

ash

#!/bin/ash

bash

#!/bin/bash

csh

#!/bin/csh

ksh

#!/bin/ksh

perl

#!/usr/bin/perl or #!/usr/local/bin/perl

python

#!/usr/bin/perl or #!/usr/local/bin/perl

sh

#!/bin/sh

tclsh (Tcl)

#!/usr/bin/tclsh or #!/usr/local/bin/tclsh

tcsh

#!/bin/tcsh or #!/usr/local/bin/tcsh

wish (Tcl)

#!/usr/bin/wish or #!/usr/local/bin/wish

zsh

#!/bin/zsh

Scripting languages such as Perl and Python use the same mechanism. For example, a script starting with the following line is clearly a Perl script:

#!/usr/bin/perl

There is a problem with software add-ons, however. If a given interpreter is not installed with the operating system, the program will likely be installed in /usr/local/bin (for commands added locally) or in some other directory, but not in /bin or /usr/bin. Linux, however, which treats nearly every software package in existence as part of the Linux distribution, almost always places these interpreters in the system directories /bin or /usr/bin. Thus, the #! comment won't always reflect the path to the shell, unfortunately.

Summary

Shell scripts are not isolated. Shell scripting was designed specifically to allow users to access their system's environment.

In this chapter, you learned:

  • Environment variables hold settings specific to your system's environment. This includes the operating system, location of commands, and user settings such as settings for the default shell and a home directory.

  • Environment variables are regular shell variables. You can access these variables with the dollar sign, $. You can store new values into these variables. But you must export each variable you want to propagate to any subshells.

  • All the data on the command line, arguments and options, are provided by the shell to your script. The shell stores these values in specially named variables called positional variables, such as $1 and $5. You can access these from within your scripts.

  • To make a script into an executable command, you need to mark the script with executable permissions and place a specially formatted #! comment in the first line of the script.

The next chapter delves into files. Just about every shell script you write will access files in one way or another.

Exercises

  1. Write a script that dies a flaming death if the SHELL environment variable is not set. Come up with a way to verify both cases, set and not set, to prove your script works.

  2. Write a script that goes through all the command-line arguments and lists each one, no matter how many arguments there are. Output only the arguments that are not empty. For example, "" is a valid command-line argument, but it is empty and so should not be listed. Output the total number of command-line arguments as well.

  3. Write a script to list out all the command-line arguments, but your script must run under the C shell, bash, ksh, and sh. (Hint: The C shell is the trickiest one.)

  4. Write a script that takes any number of directories as command-line arguments and then lists the contents of each of these directories. The script should output each directory name prior to listing the files within that directory.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.100.237