A shell script is a simple text file and can be written using any Unix text editor. Some discussion of Unix text editors can be found in the section called “Text Editors”.
Shell scripts often contain very complex commands that are wider than a typical terminal window. A command can be continued on the next line by typing a backslash (\) immediately before pressing Enter. This feature is present in all Unix shells. Of course, it can be used on an interactive CLI as well, but is far more commonly used in scripts to improve readability.
printf "%s %s\n" "This command is too long to fit in a single 80-column" \ "terminal window, so we break it up with a backslash."
It's not a bad idea to name the script with a file name extension that matches the shell it uses. This just makes it easier to see which shell each of your script files use. Table 2.1, “Conventional script file name extensions” shows conventional file name extensions for the most common shells. However, if a script is to be installed into the PATH so that it can be used as a regular command, it is usually given a name with no extension. Most users would rather type "cleanup" than "cleanup.bash".
Like all programs, shell scripts should contain comments to explain what the commands in it are doing. In all Unix shells, anything from a '#' character to the end of a line is considered a comment and ignored by the shell.
# Print the name of the host running this script hostname
Table 2.1. Conventional script file name extensions
Shell | Extension |
---|---|
Bourne Shell | .sh |
C shell | .csh |
Bourne Again Shell | .bash |
T-shell | .tcsh |
Korn Shell | .ksh |
Z-shell | .zsh |
Using your favorite text editor, enter the following text into
a file called hello.sh
.
The first step is to create the file containing your script, using any text editor, such as nano:
shell-prompt: nano hello.sh
Once in the text editor, add the following text to the file:
printf "Hello!\n" printf "I am a script running on a computer called `hostname`\n"
After typing the above text into the script, save the file and exit the editor. If you are using nano, the menu at the bottom of the screen tells you how to save (write out, Ctrl+o) and exit (Ctrl+x).
Once we've written a script, we need a way to run it. A shell script is simply input to a shell program. Like many Unix programs, shells take their input from the standard input by default. We could, therefore, use redirection to make it read the file via standard input:
shell-prompt: sh < hello.sh
Sync-point: Instructor: Make sure everyone in class succeeds at this exercise before moving on.
Since the shell normally reads commands from the standard input,
the above command will "trick" sh into reading
its commands from the file hello.sh
.
However, Unix shells and other scripting languages provide a more convenient method of indicating what program should interpret them. If we add a special comment, called a shebang line to the top of the script file and make the file executable using chmod, the script can be executed like a Unix command. We can then simply type its name at the shell prompt, and another shell process will start up and run the commands in the script. If the directory containing such a script is included in $PATH, then the script can be run from any current working directory, just like ls, cp, etc.
The shebang line
consists of the string "#!" followed by the full path name of
the command that should be used to execute the script, or the
path /usr/bin/env followed by the
name of the command. For example, both of the following are
valid ways to indicate a Bourne shell (sh) script, since
/bin/sh
is the Bourne shell command.
#!/bin/sh
#!/usr/bin/env sh
When you run a script as a command, by simply typing its file name at the Unix command-line, a new shell process is created to interpret the commands in the script. The shebang line specifies which program is invoked for the new shell process that runs the script.
The /usr/bin/env method is used for add-on shells and other interpreters, such as Bourne-again shell (bash), Korn shell (ksh), and Perl (perl). These interpreters may be installed in different directories on different Unix systems. For example, bash is typically found in /bin/bash on Linux systems, /usr/local/bin/bash on FreeBSD systems, and /usr/pkg/bin/bash on NetBSD. The T-shell is found in /bin/tcsh on FreeBSD and CentOS Linux and in /usr/bin/tcsh on Ubuntu Linux.
In addition, Redhat Enterprise Linux (RHEL) and CentOS users may install a newer version of bash under a different prefix and want to use it to run their shell scripts. RHEL is a special Linux distribution built on an older snapshot of Linux for the sake of long-term binary compatibility and stability. As such, it runs older versions of bash and other common tools. As of this writing, CentOS 7, the mainstream CentOS version, uses bash 4.2, while pkgsrc, a portable package manager offers bash 5.1.
The env command is found in
/usr/bin/env
on virtually all Unix systems.
Hence, this provides
a method for writing shell scripts that are portable across Unix
systems (i.e. they don't need to be modified to run on different
Unix systems).
Bourne shell (sh) is present and installed in /bin
on all Unix-compatible systems, so it's safe to hard-code
#!/bin/sh is the shebang line.
C shell (csh) is not included with all systems, but is virtually always in /bin if present, so it is generally safe to use #!/bin/csh as well.
For all other interpreters it's best to use #!/usr/bin/env.
#!/bin/sh (OK and preferred)
#!/bin/csh (Generally OK)
#!/bin/bash (Bad idea: Not portable)
#!/usr/bin/perl (Bad idea: Not portable)
#!/usr/bin/python (Bad idea: Not portable)
#!/bin/tcsh (Bad idea: Not portable)
#!/usr/bin/env bash (This is portable)
#!/usr/bin/env tcsh (This is portable)
#!/usr/bin/env perl (This is portable)
#!/usr/bin/env python (This is portable)
Even if your system comes with /bin/bash
and
you don't intend to run the script on any other system, using
/usr/bin/env
is still a good idea, because
you or someone else may want to use a newer version of bash that's
installed in a different location. The same applies to other
scripting languages such as C-shell, Perl, Python, etc.
Example 2.1. A Simple Bash Script
Suppose we want to write a script that is always executed by bash, the Bourne Again Shell. We simply need to add a shebang line indicating the path name of the bash executable file.
shell-prompt: nano hello.sh
Enter the following text in the editor. Then save the file and exit back to the shell prompt.
#!/usr/bin/env bash # A simple command in a shell script printf "Hello, world!\n"
Now, make the file executable and run it:
shell-prompt: chmod a+rx hello.sh # Make the script executable shell-prompt: ./hello.sh # Run the script as a command
Example 2.2. A Simple T-shell Script
Similarly, we might want to write a script that is always executed by tcsh, the TENEX C Shell. We simply need to add a shebang line indicating the path name of the tcsh executable file.
shell-prompt: nano hello.tcsh
#!/usr/bin/env tcsh # A simple command in a shell script printf "Hello, world!\n"
shell-prompt: chmod a+rx hello.tcsh # Make the script executable shell-prompt: ./hello.tcsh # Run the script as a command
There may be cases where you cannot make a script executable. For example, you may not own it, or the file system may not allow executables, to prevent users from running programs where they shouldn't.
In these cases, we can simply run the script as an argument to an appropriate shell. For example:
shell-prompt: sh hello.sh shell-prompt: bash hello.bash shell-prompt: csh hello.csh shell-prompt: tcsh hello.tcsh shell-prompt: ksh hello.ksh
Note also that the shebang line in a script is ignored when you explicitly run a shell this way. The content of the script will be interpreted by the shell that you have manually invoked, regardless of what the shebang line says.
Scripts that you create and intend to use regularly can be placed
in your PATH, so that you can run them from anywhere. A common
practice among Unix users is to create a directory called
~/bin
, and configure the login environment
to that this directory is always in the PATH. Programs and scripts
placed in this directory can then be used like any other Unix command,
without typing the full path name.