Unix Shell Scripting

Scripting : You Can Do It

“Shell scripting” sounds like a really scary topic. Isn’t scripting for uber-geeks? Unix gurus? Isn’t scripting really spooky stuff?

It isn’t if you already know how to open a command window, change directories, and copy and move files. You’ve already got the basic tools. Promise.

The only difference between a shell script and the commands you’d normally use at the command prompt is that you’ll save your commands to a file. A shell script is no more than a list of commands that are run in sequence.


First Lines

Now you’re going to write a script. You’re going to need to create a file called hello.sh .

Use the command:

vi hello.sh

If you’re using gedit or kedit, use a command like:

gedit hello.sh &
#Don’t forget that & !

To begin, a shell script has to start with a line like this:






depending on the system. This line indicates that the script should be run in the Bourne/bash shell regardless of which interactive shell the user has chosen. This is very important, since the syntax of different shells can vary greatly. Generically, the #! symbol is called a “shebang” (my preference) or a “hashpling.”

Take a good look at the line again:


It’s very easy to make a mistake right here that will cause an error:
You must not leave a space after the bang.
There are a few command interpreters that can handle it; most can’t.

Here’s a very simple example of a shell script for you to copy. It runs a few simple commands:

echo “Hello, $USER.” # list user’s files
echo “Your current directory is $PWD”

# list files
echo “It contains these files:” `ls`

Notice that this script uses a command (echo) to call a command (ls -la), and employs two environmental variables ($USER and $PWD). Not bad for a start! Now copy this script.

  1. Run this script. How do you call it?
  2. What else do you need to do to make it run?



See the comment on line three:

# list files

In a Bourne/bash script, anything following a pound sign # (besides the shell name on the first line) is a comment: the shell ignores it. It is there for the benefit of people reading the script.

$USER and $PWD are environment variables. These are standard variables defined outside the Bourne/bash shell itself, so they needn’t be defined in the script.

Variables are expanded when the variable name is inside double quotes. “Expanded” is an appropriate word: the shell sees the string $USER and replaces it with the variable’s value.



You define a variable and give it a value like this:

X=”hello you”

You get a value back out of it by referring to it as:


For instance, you can see X’s value by echoing it to the screen:

echo “$X”

Formally, $X is used to denote the value of the variable X.


No Spaces With =

Especially Note:
Your shell will be very unhappy if you leave a space on either side of the = sign.

For example, try both the following:

X = hello


Technically, you don’t need quotes unless your variable names include spaces. For example,

X=hello world # error
X=”hello world” # good

This is because the shell essentially sees the command line as a string of commands and command arguments, separated by spaces. (Read that twice more.)


is considered a command (which it is: it’s the assignment command in action). The problem with

foo = bar

is that the shell sees the word foo separated by spaces and interprets it as a command. Likewise, the problem with the command

X=hello world

is that the shell interprets X=hello as a command, and the word “world” doesn’t make any sense as its argument either.

The one critical thing you must remember about script files is this:
You must make the script file executable in order for it to run.
This means you must change its mode.


Change Mode

Make your script executable with either of the following commands:

chmod +x hello.sh


chmod 755 hello.sh

Now you can run your script with the simple command:


  1. For whom is the script now executable?
  2. Under what user’s permissions will it run?
  3. How could you avoid having to use ./ ?


Calling A Script

If a script has been made executable, you can run it simply by calling its name:




or just


There is a way around the executable limitation. If you have read access to the script, you can call the script by opening a new Bash subshell, using the script’s name as an argument:

bash ./hello.sh


This leads to a good point: you can run many kinds of script files be specifying the shell or interpreter (for instance, the Perl or PHP interpreter, or the C shell) and the script name:

perl ./perlscript.pl

csh ./cscript.sh


Shell Basics


The programming language the shell recognizes is dependant on the shell: sh, csh, tcsh, Korn shell, bash.

In some Unix environments, when users log in, the Bourne shell (or its close cousin, bash) starts up. On many systems, however, users will start in the C shell (csh).

You can change from csh to sh (or bash) simply by typing sh at the command line.

To make this change permanent, issue the command:

# then enter your password, then in Linux specify:
# if your login system is a Unix system, specify:

You may also need to check your .login and .profile files for a line like:


(Get fancy and build a grep command to do this for you.)

Change it to:


Why change your default shell? Isn’t the C shell just fine? See “Csh Programming Considered Harmful.” Simply put, the Bourne shell is the industry standard. You can do much more with sh than you can with csh. Some things can’t be done in csh at all.


Important Concept Number One: Order of Operations

Sometimes your results won’t make sense until you’re clear on the default order of operations performed by the shell.

The shell responds to a carriage return (the “Enter” key) by scanning the line and performing these operations in this order:

  1. Uses spaces to identify words (commands, options and arguments)
  2. Interprets wildcards to generate filenames
  3. Removes any quote characters
  4. Substitutes variables
  5. Substitutes embedded commands
  6. Executes the command

For example consider the command:

echo *

This will NOT echo the * onto the screen. It will provide a listing of all the files in your current directory. This is because the shell interprets the wildcard and passes the value (“all files”) as an argument to echo.


Login Scripts

Several small scripts are called upon login. They’re good examples of basic scripting.

Open the file ~/.bashrc

# .bashrc

# User specific aliases and functions

alias rm=’rm -i’
alias cp=’cp -i’
alias mv=’mv -i’

# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc

Like a polite script, .bashrc reveals its own name at the top of the script:

# .bashrc

This is different from the standard beginning for a shell script. Normally you’d see a line specifying an interpreter:


In this case you don’t, because this script is being called from a different, already-running shell script. In cases where a script is being called by another script (which is what happens with .bashrc), there may be a : character alone on the first line. Usually this is to allow a script called by one interpreter (like csh) to run under another interpreter (in this case sh). See the ITS script frzlegi.shl for an example.



The three alias declarations are defaults set (usually by the sysadmin) for some of your commands. The rm command, for instance, is forced into a different mode by the option -i .

    1. What does this option do to the function of rm ?
    2. What option for rm will override this one?

Also notice that there are no spaces before or after equal signs! Bourne/bash will gladly blow up every time you use spaces next to equal signs in normal operations.

Finally, note that you could alias more than one command, as long as they’re separated by semicolons:

alias dw=”date;who”


If clauses

Now consider the “if” clause:

# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc

In plain English, this reads something like:
“If there’s a file named /etc/bashrc, then execute /etc/bashrc.”

An if clause begins with the keyword “if,” followed by a test. If the test succeeds (for instance, some criteria is met), “then” indicates what to do next.

An if clause ends with the keyword “fi,” which is of course “if” spelled backward.



Note the way the test criteria is structured:

[ -f /etc/bashrc ]

“[” means “test.” (In fact there is a test command to which the character [ is aliased.)
What it’s testing for is the existence of a file (-f) named /etc/bashrc. If the file exists, the test is successful, and the “then” section will run. Run the command

help test

to see the test criteria available through this command.


The .

Also notice the “ . ” character:

. /etc/bashrc

Notice that there is a space between the dot and the slash!

This isn’t the dot with which you may be familiar. This dot, within the scripting context, means “execute.” In this example, it’s saying, “execute the bashrc file.” Formally, this operation is known as “sourcing.” It’s used to run a command in the existing script context; when you are running a script, for instance, this character causes the following command to run within the current context of the script, not as if it has been issued in a fresh terminal window.


Practical Uses

Consider some of the things you want to watch as a system administrator, for instance, who was logged in when. You could issue the command


to record who’s currently logged in.

    1. What does the who command return?
    2. What does the users command return?


Start Writing a Script

Now you’re going to write a script.

Open vi, pico or your favorite text editor with a new filename:

vi userlog.sh

The first line of your script must declare the bash shell as the interpreter for the script:


(Read “hash bang bin bash.”)



Remember that you must have a target log file to which your script will write. The line:

touch ~/users.log

will either update the last-modified date of the users.log file, or create it if it doesn’t exist.

On the third line, place your user-logging command:


Save the file.

Now call your script several times .

    1. What else is necessary to make it run?
    2. Under what user’s permissions will this script run?
    3. Run the command cat ~/users.log .
    4. Compare the output of cat with the output of tail: tail ~/users.log .

Your log file should look like this when you cat it:

User Log File


The test Command

The command used in most conditionals is the test command. test returns true or false (more accurately, exits with 0 or non-zero status) depending on whether the test is passed or failed. It works like this:

test operand1 operator operand2

Which ends up looking like the bold-faced part of this line:

if [ $X -lt $Y ]
# is $X less than $Y ?


There are several things to know about test.

  • While there is in fact a test command (see man test), it is typically abbreviated as ” [ ” and the character [ is actually aliased to test:

    [ operand1 operator operand2 ]

  • The ] character, on the other hand, gets no special meaning and is just there to protect programmer sanity.
  • There must – MUST – be spaces inside the square brackets! Look at the example just above. Failing to include these spaces WILL break your script.


For some tests, there need be only one operand (operand2). This looks like:

if [ -n “$empty_string” ]
# is empty_string really an empty string?


Consider a few examples:


# a simple test of inequality: is $X less than $Y: -lt
if [ $X -lt $Y ]
echo “\$X=${X}, which is less than \$Y=${Y}.”
# notice how and why we use the escape character

# a test for non-null: -n
if [ -n “$non-empty_string” ]; then
echo “non empty string is non_empty”

# test to see if ~/.bashrc exists: -e
if [ -e “${HOME}/.bashrc” ]; then
echo “you have a .bashrc file”

# is it a symlink: -L
if [ -L “${HOME}/.bashrc” ]; then
echo “it is a symbolic link”

# is it a regular file: -f
elif [ -f “${HOME}/.bashrc” ]; then
echo “it is a regular file”

# don’t forget an escape hatch: else
echo “you have no .bashrc file”

Echoing back values in test statements is one of the most powerful debugging techniques you can use.
Simply comment out any unneeded echoes when you place the script into production use.



The test command needs to be in the form


You must have these spaces, since the shell considers the first block containing no spaces to be either an operator (if it begins with a ‘-‘) or an operand (if it doesn’t).

This will trip you up. It’s a different syntax than the syntax you use everywhere else. Normally, when you’re assigning a value to a variable, you use a command like:


In this instance, though, you’re using that “operand1<space>operator<space>operand2″ syntax, which means you MUST have spaces before and after the equal sign! (Remember, this only applies to test situations.)

So for example, this:

if [ 1-eq2 ]; then #WRONG!
echo “hello”

gives exactly the “wrong” output (i.e., it echos “hello” since it sees an operand but no operator.)

Another potential trap comes from not protecting variables in quotes. There are many good reasons for using quotes all the time, or almost all of the time. Failing to do this when you have variables expanded inside tests can result in very wierd bugs. Here’s an example:

if [ $X = $Y ] ; then
echo “X=Y”
echo “Unequal!”

This will give misleading output since the shell expands our expression to

if [ -n = ]

and the string “=” has non zero length. To avoid this error, you must use quotes:

if [ “$X” = “$Y” ]

Now your shell will see:

if [ -n = “” ]


Test Operators

This is not an exhaustive list, but does contain the most often-used operators. See man test for all options.

Operator produces true if… number of operands
operand is non-zero length 1
operand is zero length 1
a directory exists named operand 1
a file exists named operand 1
the operands are integers and they are equal 2
the opposite of -eq 2
the operands are equal (as strings) 2
not equal to 2
operand1 is strictly less than operand2 (both operands should be integers) 2
operand1 is strictly greater than operand2 (both operands should be integers) 2
operand1 is greater than or equal to operand2 (both operands should be integers) 2
operand1 is less than or equal to operand2 (both operands should be integers) 2


Create if statements in your user logging script that check for the presence of your logfile and create it only if necessary.

Add echo statements to inform you of execution details at each step.

Capturing User Input

One of the most important things you can do with your script is prompt a user for input, then capture that input for your use.


Using the read command to capture user input

Frequently you will need to ask a user for some kind of input. Within a running script, for instance, you may ask for information:

echo “Please enter name of the binary file to copy to your local directory.”

The user is expected to type in that file name, then press the Enter key. Their input is available to you as long as you capture it immediately:


Now you can, for instance, echo it back:


Or you can do something with that information:

cp /bin/$FILENAME ~/

It’s important to be clear what FILENAME holds: the name of the file, not the actual file.


You may want to use something that looks more like a system prompt:

echo -n “Enter the name of an animal: “

Note the use of the -n option to prevent a new line after the echo command.


Open your script hello.sh.

Prompt the user for their name, then greet them before running the original lines of the script.

Run the script using your name.

Now run the script using a pseudo-name with a space in it. What happens?


Debug Hint: During development, you should consider echoing values back whenever they are entered, in order to confirm that you’re getting what you expect.

Now is the time to test values with spaces in them to make sure your script handles them properly!

Scripting : Exercise 1

If you’re logged in as root on a local machine, it’s time to log out. Re-log in as studenth on your local Linux computer, or as your UNM account name if you’re using ssh.

1. Create a file called names. It must be a text file, in your home directory, with the following contents:

Flintstone, Fred
Rubble, Barney
Jetson, George
Sprockets, Spacely
Bunny, Bugs
Rubble, Betty

2. Create a text file called numbers in your home directory. It will contain six telephone numbers, similar to:


(Remember, Barney and Betty are married still, we hope.) You need not copy these numbers exactly.

3. Now it’s time to use the paste command. paste joins two files line-by-line; that is, it pastes corresponding lines together. Use this command:

paste names numbers > phonebook

To see the result, command:

cat phonebook

Note that there is a tab character (inserted by paste) between the two columns.

4. paste will assemble a new file composed of the “pasted” lines of two (or more) old files. The cut command can cut this file back apart. cut can cut based on a number of characters:

cut -c1 phonebook

which returns just the first character from each line, while

cut -c1-18 phonebook

returns characters 1-18 from each line. You can also cut by tab-delimited fields:

cut -f1 phonebook

returns the first field, i.e. the name field, from phonebook. If the delimiter isn’t a tab (say, it’s a colon), you can tell cut with the -d option:

cut -d: -f1 phonebook

would give you the first field of phonebook if it was colon-delimited instead of tab-delimited. Like most commands, cut spills its output to the screen if you don’t redirect it back into another file. Try all these options.

5. Let’s make a lookup script called lu. After the “shebang,” it should read:

grep “$1” phonebook

The special variable $1 means “the first argument passed to lu.” Save the script and close it.

Call it thusly:

./lu Betty

In this case, we’re making the lu script return the line that contains the name Betty, which incidentally also contains her phone number. Voila – we’ve got a lookup script! Try it a few times.

6. Now we need a way to add numbers to our phone book. Create another script called add. After the shebang, it should read:

echo “$1        $2” >> phonebook
#that’s a tab between $1 and $2
sort -o phonebook phonebook

We’ve set ourselves up to add names and numbers to phonebook, with a tab between them, just like paste would use. Notice the sort command. By default, it sorts the lines of a file alphabetically, by the first field. You do have to use a special notation (note that -o option) to send the sorted output back to the originating file. The command:

sort phonebook > phonebook #WRONG!

will NOT work!

7. Next, we’ll build a script to remove listings. Start a new script called rem. After the shebang, it will read:

grep -v “$1” phonebook > /tmp/phonebook

mv /tmp/phonebook phonebook

The grep command with the -v option returns all lines from phonebook that do NOT contain the name we passed through the $1 variable. We have to redirect the output to a temporary file, then move it back to the original location, replacing the original file.

Give this script a try: run the command

./rem Fred

Now cat the phonebook file:

cat phonebook

It works, right? Now try:

./rem Spacely Bugs

What’s the problem with this?

8. Okay, we need to correct this issue. Copy rem to rem2 so we can try again. Just under the shebang, add:

if [ “$#” -ne 1 ] ; then
echo “Supply 1 and only 1 name to delete.”
exit 1

There’s a lot going on here! First, we’re using the special variable $# to get the number of arguments passed to rem2. If there’s any number of arguments but one, the user gets the echoed error message. Then rem2 exits with an error code of 1, without executing any of the following code.

If there’s only one argument passed with the command, rem2 does the same thing as rem.

However, what happens if we issue the command:

./rem2 Rubble Flintstone

9. So we have to make sure we’re not deleting more than one entry at once.

Copy rem2 to rem3.

Open rem3 and delete all the code after the shebang.

Replace it with this:

#Make sure we match only 1 name
matches=$(grep “$1” phonebook | wc -l)
#that’s an “el,” not a “one,” at the end

if [ “$matches” -gt 1 ] ; then
   echo “More than one name matches. Please be more specific.”
   grep -i “$1” phonebook
   exit 2
#notice we supply a second error code

elif [ “$matches” -eq 1 ] ; then
   #find and remove the listing
   grep -v “$1” phonebook > /tmp/phonebook
   #restore the file
   mv /tmp/phonebook phonebook
   echo “Success! $1 deleted.”

   echo “$1 isn’t in the phone book.”
   exit 3 #here’s our third error code

Now do some testing on rem3.


Reading Assignment

For the next class read Chapter 2 (you can skim it) and Chapter 4 (read this one carefully).


There are several very elementary techniques you can use to debug your scripts.


Switch Your Shell to Execute (Debug) Mode

Remember the Order of Operations? You can change the mode of the Bourne/bash/Korn shell to show you what your command looks like after the replacement operations are performed by commanding:

sh -x # substitute “bash” or “ksh” as necessary

You have just opened a sub-shell. You must type “exit” to leave it.

When you run a command, wildcards are replaced, variables are expanded, embedded commands are run, and then the final resulting command is actually sent to the Bourne/bash/Korn interpreter. You’ll see this resulting command, after you press the Enter key, on the line below your command, preceeded by a + sign.

You can type:

sh +x

to turn “off” debugging, but what you’ve actually done is open yet another subshell. You’ll need to command “exit” to leave this one too.


Checking Your Return Value (Error Code)

The most basic way to test for the success of a single command is to examine its error code. The error code is always returned whenever you run anything; it’s usually just not checked. Your job, then, is to check it.

First, run a command:

ls /bin

Then check its return value with this command:

echo $?

You can examine this value one command at a time, or you can include these tests within a script:

cat foo # this is “command 1” or any name you want

if [ $? -ne 0 ]; then
echo “Error at command 1!”
echo “command 1 worked”
#comment out the line above after development

This is especially valuable when you’re passing variables around:

cat $myvar # this is “command 1” or any name you want

if [ $? -ne 0 ]; then
echo “Error at command 1! \$myvar doesn’t exist.”
echo “command 1 worked”
#comment out the line above after development


Echoing Values During Development

It’s also highly useful to check the values you’re passing around by echoing them back during testing.


# test the value of USER
echo “USER is $USER”
# then comment this line out later

if [ $USER = root ]; then
echo “Welcome to FooSoft 3.0”
echo “You must be root to run this script”


Creating Your Own Error Codes

Even more useful is designating your own error codes. Yes, you do get to do that: you’re not just stuck with “0” or “1”.

if [ $USER = root ]; then
echo “Welcome to FooSoft 3.0”
echo “You must be root to run this script”
exit 666

Note that exit 666. If you use this, and test your error code:

echo $?

you’d get this if an invalid user tried to log in:



Building Debugging Into Your Script

Here’s the ultimate step: building debugging into your script. Near the top of your script, define and set a variable:

# set to 0 to turn off debugging

Then, at frequent intervals in your code, trigger messages telling you the value of variables, your position in the routine or any other information:

if [ $debugging = 1 ]; then
echo “Value of \$some_var is ” $some_var
echo -n “”

Now you can switch your debug information on and off at a single location, by changing the value of the debugging variable.

Bourne/Bash/Korn Commands

A Short Catalog of Bourne/bash/Korn Shell Commands

alias Creates an “alias” for a command and set of options alias rm=”rm -i” Set in .bashrc, forces deletes into interactive mode
cancel -a printer Removes all print requests from the current user on the specified printer cancel -a bobsprt Removes all the requests from the current user to the printer named bobsprt
cancel request_id Stops print jobs or removes them from the queue (request_ids are obtained using lpstat) cancel 5438 Stops the print job with the id 5438 whether it is printing or if it is sitting in the queue
cancel -u login_id Removes any print requests queued belonging to the user cancel -u bob Cancels all queued print requests for user bob
cat file Prints a file to the screen (all at once). Use more or less to see a page at a time. cat /etc/passwd Prints the password file to the screen.
cat file1 file2 file3 Think “concatenate” – Shows the three files in consecutive order as one document (can be used to combine files) cat cheese milk This prints the cheese file to the screen first and immediately follows it with the milk file.
cd directory Changes your current directory to the directory specified cd bin Changes directory to the bin directory
For example, if current directory=/home/users/bob/bin and you execute cd .. the new directory will be = /home/users/bob cd .. Moves you to the directory that contains the directory you are currently in
or executing cd ../.. results in the new directory= /home/users. cd ../..
cd – Moves you to the directory you just came from
cd ~ Moves you to your home directory (the directory you start from initially)
cd Moves you to your home directory (the directory you start from initially)
chexp # filename Keeps the file(s) from expiring (being erased) on the target computer for # days chexp 365 nr* Keeps the target computer from deleting all files starting with nr for 1 year (365 days)
chexp 4095 nr* Makes all files whose name starts with nr never expire or be deleted (infinite)
clear Clears the window and the line buffer clear None
compress filename Compresses the file to save disk space. none None
cp -f name target Forces existing pathnames to be destroyed before copying the file none No example or description needed
Copies a file (file1) and names the copy the new name (newname) cp old new Makes a copy of the file/directory named old and names the copy new, all within the current directory
NOTE: If you copy a file to a newfile name and newfile already exists, the newfile contents will be overwritten.
cp file dir2/ Places a copy of file in dir2/ and it retains its original name
cp ../dir1/* . Copies everything from the dir1 directory located just below where you currently are and places the copy “here” ( . ) in your current directory
NOTE: When clicking in the scroll bar, the left button scrolls down, the right scrolls up, and the middle snaps the scroll bar to the mouse position for dragging up and down.
cp -p name target Preserves all permissions in the original to the target cp -p execut1 execut2 Copies execut1 executable file and calls the copy execut2, which also has executable permissions
cp -R directory target Copies a directory and names the copy the new name (target) cp -R old/ junk/ Makes a copy of the directory named old and names the directory copy junk
cut cuts characters or fields from a file cut -f1 myfile Returns the first field (tab-delimited) of each line of myfile
cut -c1 myfile
cut -c1-8 myfile
Returns the first character, or chars 1-8, of myfile
cut -f2 myfile Returns the second field of each (tab-delimited) line of myfile
cut -d: -f1 myfile Returns the first field of a colon-delimited myfile (any delimiter may be specified)
date Writes the current date to the screen date Mon Nov 20 18:25:37 EST 2000
df system Reports the number of free disk blocks df ~ , df $HOME Both commands will print the total kb space, kb used, kb available, and %used on the home system (your system).
dir Same as ls -la ; adopted from DOS! dir
du Disk usage: du -sc Dumps information about file system size, i.e. disk usage
echo Prints text to the terminal echo “Hello”
echo *
Prints “Hello”
Prints a directory listing (surprise)
See the excellent SS64.com page.
ed The line editor utility
emacs filename Another text editor none None
eval `resize` Tells the target computer that you’ve resized the window during telnet none None
fdisk, sfdisk, cfdisk Utility for partitioning disks fdisk Opens a user interface to manage disk partitions
find Finds files on disk find / -name file_name Specify a location to start in the first argument. Use -name to search for a file name.
finger Displays info about all system users finger
finger username Displays info about username finger glenn
fsck Performs a file system check (similar to checkdisk)
grep grep is useful when you use it in a | “pipe” ps -ef | grep bob Finds all processes in full listing and then prints only the ones that match the string bob to the screen
You can also redirect its output to a file. grep -i jan b_days>mymonth Searches the file b_days for case-insensitive matches to jan and places the matching lines into a file called mymonth
grep -c string file Searches and prints only the number of matches to the screen grep -c hayes bankletter Searches the file bankletter for the string hayes and prints the number of matches to the screen
grep -i string file Searches without regard to letter case grep -i hi file1 Searches file1 for hi, Hi, hI, and HI and prints all matches to the screen
grep -n string file Prints to the screen preceded by the line number grep -n abc alpha Searches alpha for abc and prints the matches’ lines and line numbers to the screen
grep string file Searches input file(s) for specified string and prints the line with matches grep mike letter Searches for the string mike in the file named letter and prints any line with mike in it to the screen
grep -v string file All lines that do not match are printed grep -v lead pencils Prints all lines in pencils that do not contain the string lead
grep -x string file Only exact matches are printed grep -x time meetings Prints only lines in meetings that match time exactly
head file Prints the first 10 lines of the file to the screen head addresses Prints the first 10 lines of addresses to the screen
Number of lines can be modified head -25 addresses Prints the first 25 lines of addresses to the screen
help Lists shell commands (not scripts) help Prints a listing of built-in shell commands
history Prints your command history
id Prints your user id info id
info Gives help on built-in shell commands
kill -9 process_id Destroys the process with the said id kill -9 6969 PID # 6969 doesn’t have a chance here.
kill process_id Stops the process with the said id kill 6969 Kills the process with PID 6969
killall process_name Kills all processes with the provided name killall httpd Kills all httpd processes
lilo Linux Loader lilo reinstalls LILO in the boot sector, after editing lilo.conf
linuxconf The Linux Configurator linuxconf
lp (-option) file(s) Like pr, this prints designated files on the connected printer(s) (options not required and options may be combined). lp junkfile Prints the file junkfile to the default printer in default one-sided, single-sided, single-spaced format
lp -ddest file(s) Prints the file(s) to a specific destination lp -dbobsq zoom Sends the file zoom to the bobsq print queue to print
lp -nnumber file(s) Allows user to designate the number of copies to be printed lp -n5 crash Prints five copies of crash in default settings
lp -ooption file(s) lp -ohalf output Divides the paper into two halves for printing output
lp -oquarter output Prints four pages of output per side of paper
lp -olandscape output Prints output in landscape orientation
lp -oportrait output Prints output in portrait orientation
Allows printer-specific options to be used (i.e., double-sided or two pages per side, etc.) lp -od output Prints the output file double-sided on the printout
lp -obold output Prints output in bold print
lp -ttitle file(s) Places title on the banner page lp -tBobs cash Prints Bobs on the banner page of the file printout named cash
lpconfig -d queue Makes the said queue the default queue lpconfig -d vpprnt Makes vpprnt the default print queue
lpconfig printer_id queue Configures remote printers to a local print queue lpconfig prntr1 bobprt Configures a printer named prntr1 to accept print requests from a local queue named bobprt
lpconfig -r queue Removes the said queue from the local system lpconfig -r bobprt Removes bobprt queue from the local system if the person removing the queue is the owner or “root”
lpstat (-options) Prints printer status information to screen (options not required) lpstat Prints status of all requests made to the default printer by the current server
lpstat -d Shows the default printer for the lp command none None
lpstat -r Lets you know if the line printer scheduler is running none None
lpstat s Prints the queues and the printers they print to none None
lpstat -t Shows all print status information none None
lpstat -u”user1, user2″ Prints the status of requests made by the specified users lpstat -u”bob” Prints status of all requests made by the user with the id bob
ls (-option-optional) Lists all the nonhidden files and directories ls Lists all nonhidden files and directories in the current directory
ls (-option-optional) ls bin Lists all nonhidden files and directories in the bin directory
ls -a Lists all files and directories including hidden ones ls -a Lists all files and directories, including hidden, in the current directory
ls -a temp Lists all files and directories in the temp directory.
ls -al NOTE: Options can be combined using ls Lists all files (including hidden (-a)) in long format (-l)
ls -l or ll Lists all nonhidden files and directories in long format ls -l Lists all nonhidden files and directories in the current directory in long format
ls -l work Lists all nonhidden files and directories in the work directory in long format
ll work
ls -r Lists all files and directories in reverse alphabetical order ls -r Lists all nonhidden files and directories in the current directory in reverse alphabetical order
ls -r abc Lists all nonhidden files and directories in the abc directory in reverse alphabetical order
ls -t Lists all nonhidden files in the order they were last modified ls -t Lists all the nonhidden files in the current directory in the order they were last modified from most recent to last
ls -t work Lists all the nonhidden files in the work directory in the order they were last modified from most recent to last
make compilecommand Performs the pre-compile and compile functions make Used at several stages of compilation. Used in preparing a custom Linux kernel.
minicom Opens a graphical utility in a shell, which is used to test PPP connections like modems.
mkdir dirname Creates a directory mkdir junk Makes a directory named junk in your current directory
You can also designate where the directory is to reside. mkdir ~/left Makes a directory in your home directory named left
modprobe Adds a kernel module while running

modprobe module

modprobe -r module

modprobe -a

modprobe -k

Loads module

Removes module

Lists all modules

Auto-cleans all modules

more input This prints to screen whatever is input-useful because it only shows one screen at a time. more groceries This will list the groceries file to the screen.
scroll bar continues to the next screen
return moves one line forward
Q quits
G goes to the end
1G goes to the beginning
Ctrl u moves up ½ screen
Ctrl d moves down ½ screen
mount drive Mount a drive mount /mnt/cdrom Mounts the CDrom drive
mouseconfig Sets up the mouse mouseconfig
mv initial final Renames files and directories mv temp script_1 Renames the file (or directory) temp to the name script_1 in the current directory
Also moves files to other directories mv script.exe ~/bin Moves the script.exe file to the bin directory that is in the home (~) parent directory and it keeps its initial name
You can do multiple moves. mv script_1 script.exe ~/bin Moves both script_1 and script.exe to the bin directory
netcfg Configure the network; not found on all Linux systems, but common on other Unix like Solaris netcfg
nislookup Returns DNS information nislookup google.com Returns the IP address if DNS is working
dhclient DHCP client program
passwd Change your password pswd
paste Paste corresponding lines of files together paste file1 file2 Outputs line 1 of file1 “pasted” to line 1 of file2, and so forth line by line
pr -h “header” filename Prints the file with a specified header rather than the filename pr -h “users” userlist Prints userlist with users as the header
pr (option) filename Prints the specified file to the default printer (options are not required but can be combined in any order) Options can be combined using pr pr userlist Prints the contents of userlist to the default printer
pr +k filename Starts printing with page k pr +5 userlist Prints the contents of userlist starting with page 5
pr -a filename Prints in multicolumns across the page (use with -k) pr -3a userlist1 Prints userlist in three columns across the page
pr -d filename Prints in double space format pr -d userlist Prints userlist with double space format
pr -k filename Prints in k columns pr -2 userlist Prints the contents of userlist in 2 columns
ps Shows certain information about active processes associated with the current terminal ps Shows a listing of process IDs, terminal identifier, cumulative execution time, and command name
ps -e Shows information about all processes ps -e Shows a listing of process IDs, terminal identifiers, cumulative execution time, and command names for all processes
ps -ef Shows all processes in a full listing ps -ef Shows all current processes in full listing
ps -f Shows a full listing of information about the processes listed ps -f Shows UID (user or owner of the process), PID (process ID–use this number to kill it), PPID (process ID of the parent source), C (processor utilization for scheduling), STIME (start time of the process), TTY (controlling terminal for the process), TIME (cumulative time the process has run), and COMMAND (the command that started the process)
ps -u user_id Shows all processes that are owned by the person with the pertinent user_id ps -u bob Shows all the processes that belong to the person with the userid bob
pump RH7 and older

A BOOTP and DHCP client daemon

pump Renews DHCP lease
pwd Prints the current directory to the screen pwd May print something like “/home/bob”
qstat Displays the status of a process that has been submitted the Network Queuing System (basically a batch job) qstat Shows the status of the requests submitted by the invoker of the command-this will print requestname, requestid, the owner, relative request priority, and request state (is it running yet?)
qstat -a Shows all requests
qstat -l Shows requests in long format
qstat -m Shows requests in medium-length format
qstat -u bob Shows only requests belonging to the user bob
qstat -x Queue header is shown in an extended format
read Captures user input echo -n “Enter the name of an animal: “
echo -n “You are a(n) $ANIMAL”
restart /etc/init.d/nfs restart Restarts a service, in this cast the nfs
rm -f file1 file2 rm xyz abc Deletes the files named xyz and abc
rm * Deletes everything nonhidden
Forces deletion without prompt regardless of permissions rm -f program Removes the file program without regard to permissions, status, etc.
rm file1 file2 file3 Removes (deletes) file(s) NOTE: Options can be combined using rm rm xyz Deletes a file named xyz
rm -fR name, rm -Rf name ****dangerous**** This combination will force the removal of any file and any directory including anything inside of it rm -Rf c_ya Forces removal without prompts of the c_ya directory and anything inside of it
rm -i file1 file2 Prompts before deletion of files. *******USE -i AT FIRST******* rm -i * Prompts at each nonhidden file and lets you decide whether or not to delete it
rm -r directory, rm -R directory Remove a directory along with anything inside of it rm -r bin , rm -R bin Each of these will remove the bin directory and everything inside of it.
rm -Ri directory Deletes the contents of a directory and the directory if it is empty by prompting the user before each deletion rm -Ri rusure Deletes anything in the directory called rusure that you verify at the prompt, and if you remove everything in the directory, you will be prompted whether you want to remove the directory itself or not
rmdir directory Removes a directory like rm -r does if the directory is empty rmdir bin Removes the bin directory if it is empty
rmdir -p directory Removes a directory and any empty parent directories above it (-pi does the same thing but it prompts before each removal) rmdir -p /home/bin/dir1 Deletes the dir1 directory; if bin directory is empty, it is deleted, and if home directory is empty it is also deleted
sed The stream editor sed ‘s/Unix/UNIX/’ source_file/g Replaces Unix with UNIX in source_file
sort infile Sorts the contents of the input file in alphabetical order sort names Sorts the contents of names in alphabetical order
tail file Prints the last 10 lines of the file to the screen tail test.txt Prints the last 10 lines of test.txt to the screen
Number of lines can be modified here, too tail -32 test.txt Prints the last 32 lines of test.txt to the screen
test expression Tests a condition, and returns 0 (success) if it passes, non-0 if it fails test See Operators for a fuller listing
Used in if operations if test $name = Julio; then
test string = string
test string != string tests inequality
if test string ; then tests for the existence of string
test -n string string is not null
test -z string string is null
test int1 -eq int2 int1 equals int2
test int1 -ge int2 int1 greater than or equal to to int2
test int1 -gt int2 int1 is greater than int2
test int1 -le int2 int1 is less than or equal to int2
test int1 -lt int2 int1 is less than int2
test int1 -ne int2 int1 is not equal to int2
test -d file file is a directory
test -e file file exists
test -f file file is a regular file
test -r file file is readable by the process
test -s file file has nonzero length
test -w file file is writable by the process
test -x file file is executable
test -L file file is a symbolic link
timeconfig Configure the time service timeconfig
touch Update the last-modified date of the file named touch myfile If myfile exists, last-modified date is updated to now; if myfile doesn’t exist, it is created
type Find out where a bash command is taken from (cf. which); tells if an executable is a binary, script or link type bash bash is /bin/bash
type -a Print all locations of a command type -a bash
uncompress filename Expands a compressed file none
uptime Displays time the system has been running uptime
useradd Add a user to the domain useradd
users Lists current users users Lists all users on a single line
vi filename Text editor that exists on every UNIX system in the world none
vuepad filename Opens filename for editing/viewing in the vuepad editor none
which Prints the locations of shell commands (cf. type)
who Tells you who is logged onto your server who -imH Lists each user on a separate line, along with session information
who am I Tells you your user information who am I , whoami
Xconfigurator An X-Windows configuration tool Xconfigurator This opens an interface to configure your video card and monitor.
xterm Opens a new window (x-terminal) for you to work xterm This opens another window like the one you are currently working in.
Note: The size of the window takes precedence over position, so if you position it too close to the side of the screen, it will position at the edge with the correct size. xterm -geom 5×5+0+0 The third command will make a 5 by 5 window and position its top left-hand corner at the top left-hand corner of the screen. xterm will not compromise size when positioning.
xterm -bd huntergreen The second command sets the window border color to huntergreen.
xterm -fg red
Position +0+0 is the top left-hand corner of the screen, and the bottom right is approx. +1200+1000 depending on your resolution. xterm -geom 10×35+300+500 The second command will open a window 10 pixs wide by 35 pixs tall and position its top left-hand corner 300 pixs from the left edge and 500 pixs down from the top.
xterm -(areas) color Allows you to modify different colors in your xterm window xterm -bg white The first command sets the background color to white.
xterm +option +option resets the option to default Options can be combined using xterm
xterm -e program Executes the listed program in the new xterm window-when the program is finished, the new xterm window goes away xterm -e myprog.exe This opens an xterm window and executes the program myprog.exe from that window so that you may still work in your present window.
xterm -fn font Sets the font in the new xterm window xterm -fn courr18 Sets the font to courr18 (default is fixed)
xterm -geom xxy+px+py This option allows you to specify the size x pixels by y pixels and placement position x by position y of the new window when it opens. xterm -geom 80×80+0+50 The first command will open a window 80 pixels wide by 80 pixels tall and position its top left-hand corner at 0 pixels to the right of the left edge and 50 pixels down from the top of the screen.
xterm -help Displays the xterm options xterm -help Shows the options available
xterm -iconic Starts the new xterm as an icon (double-click to maximize) xterm -iconic -title xyz Opens an xterm in iconic form with the title xyz
xterm -sb Opens an xterm that saves a set number of lines when they go off the top of the page and makes them accessible with a scroll bar xterm -sb Puts a scroll bar on the right side of the page for reviewing past lines in the window
xterm -sl number Specifies the number of lines to be saved once they go off the top of the screen (default is 64) xterm -sl 1000 The xterm will save 1,000 lines of work once it has moved off the immediate viewing area; it can be accessed using the scroll bar.
xterm -title label Allows you to label your window’s top title bar xterm -title SCRIPTS Opens an xterm window with the title SCRIPTS (default is whatever follows the -e option)

Shell Variables

In Unix there are three kinds of variables: System Variables (you normally can’t change these), Environment Variables (which are yours, and specific to your environment) and Shell Variables (which you declare and use in programming).

Bourne, bash and Korn allow you to have variables, just like any programming environment. In Bourne/bash/Korn, though, variables do not need to be declared. To create and assign a value to a variable, command:


To use the value of the variable later, use




The latter syntax is useful if the variable name is immediately followed by other text.


Showing All Variables

Display your variables with this series of commands:

set shows all variables (local and exported)
env shows only environment- and user-exported (whole-environment) variables (not local)
export shows exported variables
unset removes a variable
declare also shows all env vars


Setting Variables

You can create or set variables to new values simply by assigning a value:


To get the value back from the variable, place a $ before it:

echo $HOME

Which returns:


Note that

echo HOME

will get you:


If there is a space in the variable’s value, use double quotes to preserve them (and to allow them in the first place):

fullname=”John Doe”

Often in scripts or commands, you’ll need to use environment variables in text without spaces. This can confuse the shell.

Try these lines:

mycolor=blue; export mycolor
echo “My dog is $mycolorish.”

Now try:

echo “My dog is ${mycolor}ish.”

Remember the uses of the escape character \ .

myval=50 #(you just assigned a variable)

Now display $50 (fifty dollars):

echo \$$myval



If you refer to a variable that hasn’t been defined, the shell substitutes the empty string. Try this:

echo aaa $FOO bbb
echo xxx${FOO}yyy

What do you get?


Environment Variables

At the command line, type:


to see the environment variables available to you. Notice particularly the MAIL, PATH, HOME and LOGNAME variables, which can be useful in scripting.

To add permanent environment variables, edit the .bashrc or .profile file in your home directory:

vi .bashrc

and add your variable name and value:

export userlog


Exporting Variables

Declaring and populating a variable makes its value available to the current shell process, but other shell processes you might be running would not be able to access them. To make a variable available system-wide, you need to export it:

MYVAR=/var/user.log; export MYVAR
#this will work in Bourne/bash/Korn

export MYVAR
#this will work in Bourne/bash/Korn

export MYVAR=/var/user.log
#this will only work in bash

Declare and export a variable.

Use echo to see if the variable’s value is available in your current shell.

Now open another shell and test with echo again.  Did your variable export?


“Automatic” Variable Export to Subshells

Another of many differences between Bourne and bash is that bash supports automatic variable export to subshells:

Variables present in the shell’s initial environment are automatically exported to child processes. The Bourne shell does not normally do this unless the variables are explicitly marked using the export command.

Create and populate a new variable. Do NOT export it.

Now open a new subshell using the bash, sh or ksh command.

Use echo to see if the variable’s value is available.


Using Quotes to Enclose Variables

Sometimes you’ll need to protect variables in double quotes. This is usually important when your variable’s value either (a) contains spaces or (b) is the empty string. Consider this:

if [ -n $X ]; then
# -n tests to see if the argument is non empty
echo “the variable X is not the empty string”

This script will return:

the variable X is not the empty string

Why? Because the shell expands $X to the empty string. The expression [ -n ] returns true because it is no longer provided with an argument! See Order of Operations for an explanation; the short one is that the shell “got to” the variable first. Put your variables in quotes to ensure you get what you expect:

if [ -n “$X” ]; then # -n tests to see if the argument is non empty
echo “the variable X is not the empty string”

In this example, the expression expands to [ -n “” ] which returns false, since the string enclosed in quotes clearly is empty.


Dealing With Spaces in Variable Output

Note that if the value of a variable will return with embedded spaces, you need to protect it with quotes. Try this example now:

color1=”red chile”
color2=”green chile”
for X in $color1 $color2 $color3
echo $X

  1. Run this script.
  2. What did you get?
  3. How can you fix this?

Here’s the point: variables should be protected with quotes unless you are sure that their value does not contain any spaces.


Variable Substitution (quasi-variable constructs)

Sometimes in scripting you won’t know if a variable has been set, or if it’s non-null. You can perform variable substitution so that you can handle this contingency in code.

${VAR:-expression} #assume expression equals “3”
Use default value: if VAR is set and non-null, expands to $VAR. Otherwise, expands to expression.

Set default value: if VAR is set and non-null, expands to $VAR. Otherwise, sets VAR to expression and expands to expression.

If VAR is set and non-null, expands to $VAR. Otherwise, prints expression to standard error and exits with a non-zero exit status.

If VAR is set and non-null, expands to the empty string. Otherwise, expands to expression.

Expands to the length of $VAR.

The above patterns test whether VAR is set and non-null. Without the colon, they only test whether VAR is set.

IO Redirection

By default, every process has three associated file descriptors:

  • standard input (0),
  • standard output (1) and
  • standard error (2).

For these,

  • Your keyboard is the default source of standard input.
  • Your monitor is the default target for standard output, and
  • Your monitor is also the default target of standard error (which is why you even see those error messages).

The critical thing to know is that a command’s input and/or output can be redirected to another command or to a file.



Consider the ways you can do this:

Send Standard Output To a File

> file_name # or 1> file_name
Connect standard output to the file file_name. This allows you to save the output of a command to a file. If the file doesn’t exist, it is created. If it does exist, it is destroyed or “clobbered” and then re-created:

who > users.log

who 1> users.log

If no number precedes > or >>, the shell assumes you’re redirecting Standard Output, i.e. 1.

How do you check the success of output redirection?

who > users.log || echo “who did not redirect to users.log!”


Get Standard Input From a File

< file_name
Connect standard input to the file file_name. This allows you to have a command read from the file, rather than entering input by hand. Note that there is a difference in the output of:

wc -l users.log
  2 users.log


wc -l < users.log

Sending nothing to a file, like this:


will clear the contents of the file. This is a shell “shortcut” that is sometimes used to clear log files.

How do you check the success of input redirection?

wc -l < users.log || echo “who did not redirect to wc -l!”


Append Standard Output to a File

>> file_name
Connects standard output to the file file_name. Unlike >, however, the output of the command is appended to the file. If the file doesn’t exist it is created.

who >> users.log

who 1>> users.log

wc -l < users.log

How do you check the success of append redirection?

who >> users.log; tail users.log
#comment out the second half of this line later


Accept Standard Input Until a Keyword Appears


This construct causes standard input to come from a file, but only until key_word appears on a line by itself. There must be no space between << and key_word.

By default, << acts like double quotes (i.e., variables are expanded). If, however, key_word is quoted, then << acts like single quotes.

How do you check the success of keyword redirection?

Hamlet.txt<<Denmark; cat Hamlet.txt
#comment out the second half of this line later


Redirect Standard Error (2) to a File

You can capture error messages that would otherwise be printed to the screen. This is good for logging the success of many kinds of user interactions. To see this compare the output of:

cat foo > users.log


cat foo > users.log 2>> errors.log

(run cat errors.log to see the result).

(What did we just do here?) Note that I/O redirections are parsed in the order they are encountered, from left to right. This allows you to do fairly tricky things, including throwing out standard output, and piping standard output to a command.

How do you check the success of StdErr redirection?

cat foo > users.log 2>> errors.log

cat users.log; cat errors.log #comment out this line later


Send 0, 1 or 2 to 0, 1 or 2


2>&1 #Sends standard error (2) to standard output (1)

Think of this as meaning something like, “Send standard error to the same place as standard output.”

This formulation maps any file descriptor to any other: digitA is sent to digitB. Now is a good time to recall that standard error is usually sent to the terminal screen, even if standard output is being written to a file or piped to another command. You can capture error messages that otherwise would flash on the screen and then be lost with code like:

echo “Error: Process Parameter(s) Missing.” 1>>log.file 2>&1

In this case the echoed message isn’t written to the terminal screen; it’s written to log.file. Notice that this is a two-step process.

First, standard output from the echo command is sent (via 1>>, though >> could have been used) to log.file.

Then standard error is also sent to standard output, which writes it to log.file as well. The example above shows how to map standard output and standard error to the same file.


You must use this notation when you send both Standard Output and Standard Error to the same file:

echo “Error: Process Parameter(s) Missing.” 1>>log.file 2>&1

If you try this notation:

cat foo 1>test.log 2>test.log

you will experience data loss!


Here’s one way to send 1 and 2 to different files:

cat foo 1>>test.log 2>>error.log

Run cat test.log and you’ll see it’s empty (assuming you don’t really have a file named foo), while error.log contains the error message.

This construct is also useful for printing error messages:

echo “Danger! Danger Will Robinson!” 1>&2

If you run this line in the middle of a procedure, even if output is otherwise being written to a file, the echoed error (or “danger”) message will print to the screen anyway.


Send 0, 1 or 2 to Standard Input


Use file descriptor digit as standard input. For example, <&1 sends standard output (1) to standard input (0). This is the same as simply using < if you are only using <&1 , but <&2 is of course very different.


Close Standard Input or Output

Close standard input.

Close standard output.


Any number of commands can be pipelined together.

command1 | command2

The above command creates a pipe: the standard output of command1 is connected to the standard input of command2. Any number of commands can be pipelined together.

Any command that can accept Standard Input and produce Standard Output is called a filter command.

This is functionally identical to

command1 > /tmp/foo
command2 < /tmp/foo

except that no temporary file is created, and both commands can run at the same time.


Start a new script named pipes. Be sure to include the usual shebang.

Prompt the user for a user name, and capture that user name.

Check the file /etc/passwd to see if that user is present using grep.


Conditional “And:” &&

command1 && command2

Executes command1. Then, if it exited with a zero (true) exit status, executes command2.


Modify the pipes script to pipe the output of grep to wc -l.

Use && to echo a message back to the user if the user name they supplied is in the list.

Test the script with valid user names.


Conditional “Or:” ||

command1 || command2

Executes command1. Then, if it exited with a non-zero (false) exit status, executes command2.


Modify the pipes script to echo a message back to the user if the user name they supplied is NOT in the list.

Test the script with invalid user names.


The tee Command

Need to “split up” Standard Output so you can send it to both Standard Output and a file? The tee command is for you.

cat /var/log/messages | tee newfile | less

Just pass stdout through tee, write it to the file of your choice, and send the same stdout to the next command. See intricate examples at


or a brief discussion at


Try This:

Develop a command that first creates some output, pipes it to tee, which writes it to a file and also back to the terminal display.


Modify the pipes script to echo a message back to the user if the user name they supplied is NOT in the list, and also write an error to an error log, errors.log.

Debug Hint: Whenever you can, log everything, especially during development: failures, successes, and to any degree possible, variable values and executed actions.

The && and || conditionals make this much easier.