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
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.
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.
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.
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
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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 |
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.
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.
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
.
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.
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.
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.
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.
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.
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.
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.
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.
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.)
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.
3.144.100.237