Revised February 3, 2001. Press your browser's Reload or Refresh button to get the latest version.
A shell is an operating system command interpreter. A Unix shell is also a programming language for manipulating files and programs. You can put shell commands in a file called a shell script and then run the script just like any other program.
Shell scripts are useful for automating repetitive operations, such as running a program on a large collection of input files. You can use shell scripts as "wrappers" to adapt and customize compute-intensive programs (in C, Lisp, ...) for the local file system. Shell scripts start up and configure Unix itself when you turn on the computer; other shell scripts configure your session when you log in.
There are several Unix shells: sh came first, csh and then tcsh were popular for a time, now bash is the default login shell in most Linux installations, including the ACC lab machines at Evergreen. I strongly recommend bash for shell programming as well. It is like sh but also includes many conveniences inspired by csh and tcsh. And, if you use the same shell for programming and interactive use, you can try out each command in your program just by typing at the command prompt.
The sh/bash command prompt is usually a dollar sign $ while the csh/tcsh prompt is usually a percent sign %.
The chapter on shell programming (lesson 14) in the Ten Minutes textbook inexplicably uses csh/tcsh not sh/bash. Ignore that chapter and use the Nutshell book instead (chapters 6 and 7 are on bash).
There are two kinds of shell commands, which are described in two different places.
Most shell commands, for example ls, cat, more etc., etc. are just programs in some system directory such as /bin or /usr/bin. These work the same way in any shell. There is an alphabetic listing in chapter 3 in the Nutshell book and you can get on-line information using man ls etc.
Other shell commands, for example cd and pwd, are built into the shell program itself. Some of these built-ins only exist in some shells, or work differently in different shells. There is an alphabetic listing of the built-in commands for bash in chapter 7 in the Nutshell book; to get on-line information you must use man bash.
The shell evaluates some command arguments before passing them to the command. For example, it expands the asterisk or star * to a list of the files in the current working directory. The echo command simply evaluates its arguments. Use it to see the effects of evaluation. For example:
$ echo hello hello $ echo * #shell.html# emacs-intro-dl.html emacs-intro.html emacs-intro.html~ emacs-telnet files-outtakes.html fofc-announcements.html ...
Create and assign variables using the equal sign = (with no spaces around it). To substitute a variable (obtain its value), you must precede it with the dollar sign $.
$ x=3 $ echo x x $ echo $x 3
Ordinary variables are like local variables: they are only recognized in the script where they are defined (more accurately, they are only recognized in the process where they are defined; usually each script and program executes in its own process). Environment variables are like global variables: they are recognized in other processes (scripts and programs) as well. Use the export command to make environment variables.
export y=4
Unix provides a set of built-in environment variables that define the configuration for your session: your home directory, your path etc. These variables are spelled in all capital letters. Use the printenv command to display your environment (no $'s needed, omit the argument to see all environment variables).
$ printenv y 4 $ printenv PATH /usr/local/bin:/usr/bin:/bin:/usr/X11R6/bin:/home/jon/bin $ printenv ... y=4 PATH=/usr/local/bin:/usr/bin:/bin:/usr/X11R6/bin:/home/jon/bin HOME=/home/jon SHELL=/bin/bash ...
Sometimes you need to suppress evaluation by the shell so you can use a string that contains special characters as an argument to a command. Use single quotes '...' to suppress all evaluation by the shell. Use double quotes "..." to suppress most evaluation including wildcard expansion but allow variable substitution. Use backslash \ to suppress ("escape") interpretation of individual characters.
$ echo The value of x in *.1 is $x The value of x in script.1 is 3 $ echo 'The value of x *.1 is $x' The value of x in *.1 is $x $ echo "The value of x in *.1 is $x" The value of x in *.1 is 3 $ echo "He said "The value of x in *.1 is $x"" He said The value of x in script.1 is 3 $ echo "He said \"The value of x in *.1 is $x\"" He said "The value of x in *.1 is 3"
Use backquotes `...` to interpolate the output from one command into another command.
$ date Sat Feb 3 12:12:02 PST 2001 $ echo Now it is `date` Now it is Sat Feb 3 12:12:28 PST 2001
When using bash interactively, you can edit the command line using arrow keys, backspace, delete, and emacs-style commands (C-a to move to the beginning of the line, C-e to the end, C-k to cut, C-y to paste or "yank"). Use the up-arrow key to retrieve the previous command for execution or editing, use the up- and down- arrow keys to scroll throught the history of recent commands.
Use the backslash character at the end of the line to create long commands that extend over more than one line (this just uses backslash to escape the usual interpretation of the newline as a signal to execute the command). The shell prints a different prompt character (usually >) to indicate continuation lines.
$ echo This command \ > extends over \ > several lines This command extends over several lines
In addition to producing output, all commands return a status value, where 0 indicates success and any nonzero value indicates failure. The shell does not normally display the status value but the status returned by the last command is also the value of the variable $?. Status values are used to control the execution of shell scripts.
$ ls index.html index.html $ echo $? 0 $ ls nowhere ls: nowhere: No such file or directory $ echo $? 1
The conditional operators are && (and) and || (or) test command status to determine whether to execute the next command. In cmd1 && cmd2, cmd2 executes only if cmd1 succeeds. In cmd1 || cmd2, cmd2 executes only if cmd1 fails. In cmd1 && cmd2 || cmd3, cmd2 executes if cmd1 succeeds and cmd3 executes if it fails.
$ file=index.html $ ls $file && echo All is well || echo Something is wrong index.html All is well $ file=nowhere.html $ ls $file && echo All is well || echo Something is wrong ls: nowhere.html: No such file or directory Something is wrong
The test command is used to control execution of shell scripts. It computes and returns a status value but produces no output. Use command options to select the condition to test. For example the -a option tests if a file exists.
The command test condition can be abbreviated [ condition ]
$ file=index.html $ test -a $file && echo $file exists || echo $file does not exist index.html exists $ file=nowhere.html $ [ -a $file ] && echo $file exists || echo $file does not exist nowhere.html does not exist
The if command provides another way to provide conditional execution. You can put many commands in each block (after the then or after the else). The shell can tell when commands like this are incomplete so you needn't put backslashes at the end of lines (however, these commands do seem to be sensitive to where you put the linebreaks).
$ if [ -a $file ] > then > echo $file exists > else > echo $file does not exist > fi nowhere.html does not exist
To write a shell script, just put commands in a file (using some text editor such as emacs). There are several shells so the first line in the file should identify the shell language you use in your script. For bash, the line is
#!/bin/bash
After the first line, the pound sign # indicates a comment. The shell ignores everything starting at the pound sign through the end of the line.
Usually, shell script files do not have any extension (any dot etc.) at the end of the name.
There are two ways to run shell scripts.
The easiest way is to source the script. Just execute the command source file (or just . file). You can type this command in your interactive session or put this command in another script. This causes the shell to execute the commands in file in the current process, just as if you had typed them then and there, so ordinary (un-exported) variables defined in the script can be used by subsequent commands in the same process. You only need read permission for file (you don't need execute permission) and file need not be in a directory that is in your path.
Another way is to treat the script as a full-fledged Unix command. Just execute the command file. This causes Unix to execute file in its own process, so unexported variables cannot be used by subsequent commands in the process where the command was issued. You must have execute permission for file, and file must be in a directory that is in your path.
In most Linux setups, your working directory . is usually not in your path (there are security reasons for this), but your own ~/bin directory is in your path. This is a good place to put your shell scripts once you've got them working the way you want.
The for command enables you to repeat a sequence of commands for each item in some collection.
$ for i in 1 2 3 > do > echo $i > done 1 2 3
You can use for to perform sn operation on every file in some collection by using the file glob character star *.
$ for file in *.html > do > echo The file is $file > done This is file emacs-intro-dl.html This is file emacs-intro.html This is file files-outtakes.html ...
You can invoke scripts with arguments. The special variables $1, $2, $3, etc. are the first, second, third etc. command line arguments.
You often need to take apart and create filenames in scripts. The dirname command outputs the directory part. The basename basename command outputs the filename part, and takes an optional argument that can remove the extension.
$ file=/usr/users4/fofc/z2html/tex/dcs.tex $ dirname $file /usr/users4/fofc/z2html/tex $ basename $file dcs.tex $ basename $file .tex dcs
These commands are often used with backquote to create new filenames.
$ echo We have `basename $file` but we want `basename $file .tex`.html We have dcs.tex but we want dcs.html
The read command reads one line from the standard input and assigns its contents to variables. The read command returns 0 (success) until it reaches end of file, so it can be used with the while command to read files one line at a time.
There are other control structures including case and until.