Computer Science 010

Lecture Notes 10

Shell Scripts and

Customizing X

Shell Scripts

Compiled programs are not the only way to get things done in Unix. Many tasks can be done using shell scripts: lists of commands to be executed by the command shell.

A shell script is a program, but unlike a C program which needs to be compiled before you can run it, the script is interpreted by sending the commands in the script to the appropriate shell program.

Shell scripts come in many types, with the most important distiction being what shell program is intended to interpret the program. Common unix shell programs include the Bourne shell (sh), the C shell (csh), the Bourne-Again shell (bash), the T-C shell (tcsh), the Korn shell (ksh), and the Z shell (zsh). You may see all of these used as the interpreter for shell scripts you run across. Unfortunately, each has a different syntax, but the basic functionality is similar. More elaborate scripting languages are also available, most notably Perl, which is an extremely flexible scripting language.

A shell script typically specifies which shell program should be used to interpret it by specifying the shell on the first line. For example, if you look at your .xinitrc file, the first line is:

#! /usr/local/bin/bash
which specifies that /usr/local/bin/bash should be used to run the script. Most shells use the # character to indicate a comment, but #! is interpreted as a special sequence to be followed by a shell name. If this line is left off, your default shell will be used, which for us is bash. We will use bash for our shell scripting examples.

We start with a very simple script, which just executes a collection of Unix commands that we tend to execute repeatedly. We all know that make is better than shell scripts if you want to automate the compiling and linking process, but here's a script which would compile and link a C program in two source files, file1.c, and file2.c, with the resulting executable named prog.

#! /usr/local/bin/bash
# Simple script to compile files.  Don't do this - use make
gcc -c -g -Wall file1.c
gcc -c -g -Wall file2.c
gcc -o prog -g file1.o file2.o
If we store this in a file called compile_all and give it execute permission, we can run it just like we run a compiled C program.

As with make, we can define variables in our compile_all script:

#! /usr/local/bin/bash
# Simple script to compile files.  It's a little better, but still use make
CFLAGS="-c -g -Wall"
$CC $CFLAGS file1.c
$CC $CFLAGS file2.c
$LD $LDFLAGS -o prog file1.o file2.o
There are a few things to point out about this script. First, variables are set like in a Makefile, with double quotes required to set a value containing whitespace. You can refer to a variable by preceding its name with a $.

The ability to group commands like this is helpful in itself, but much more is possible. You can use pipes and I/O redirection in scripts. If you have a complex command line where several steps are needed to pipe the output from one command to the next, you can create a script to avoid retyping the command line.

You can pass arguments from the command line to your script. Try this one:

#! /usr/local/bin/bash
# Simple script to compile files, taking the names as command-line arguments
# Still use make.
CFLAGS="-c -g -Wall"
$CC $CFLAGS $1.c
$CC $CFLAGS $2.c
$LD $LDFLAGS -o $3 $1.o $2.o
Running this at the command prompt with compile_all file1 file2 prog would result in the same functionality as the previous example. We see that the command line arguments can be referenced as $1, $2, $3, etc.

Shell scripts are much more powerful when we start to take advantage of the available control structures. These include all of the usual control structures you'd expect to see in a programming language:

if  list; then list; 
[ elif list; then list; ] 
[ else list; ] 

while list; do list; done

until list; do list; done

case word in [ ( pattern [ | pattern ] ... ) list ;; ] ...

for name [ in word ] ; do list ; done
The syntax for these is a bit different than what you're used to, but each of these does what you'd expect.

You can do things like check the value of a variable:

if [ $CC == "gcc" ]; then
  echo "We are using gcc"
  echo "We are not using gcc"
Or check for the existence of a file:

if [ -f "file1.c" ]; then
  echo "We have a file file1.c"
  echo "file1.c not found"
A frequently useful operation is to do something with each of a number of files. For example, to copy each C file in a directory to have a backup copy with a .bak extension:

for file in *.c; do
  cp $file $file.bak
The best way to learn about bash shell scripts is to look at larger examples and the bash man page. A simple example is the .bashrc file in your home directory. This is a bash script that gets executed every time you log in. This is a relatively straightforward script, but includes examples of how to write functions in a bash script. A shell script is also used to start up the FreeBSD systems we use. Take a look at the file /etc/rc on one of the FreeBSD systems and find out what happens when one of these systems is booted.

Assigning the output of a program to a variable

You can run a command and save its output to a shell variable. For example, to save the output of wc for a given file in the variable count:

Combining this with the awk utility that we will discuss next can be very powerful.

More Unix Utilities

There are a few more very useful Unix utilities worth looking at. We will again list them here, look up their man pages, and try them out

Customizing X

X windows offers you many opportunities for customization. X is really a programming library that allows one to build graphical user interfaces on Unix. Applications that are written with the X library normally define a collection of resources and allow users to set the values of those resources. Common resources are the colors of things and the size and location of windows that are created. We can also define some programs to execute whenever we start X. Finally, we can change the look-and-feel of X by changing which window manager we use. We will look at each of these in turn.

X Application Arguments

If you look at the man page for applications written with X, they will typically contain command line arguments that allow you to modify the appearance of the application. Typical arguments include colors, location, and size. If you look at the man page for X, it will show you the options that most X applications use. Here are some options that control color:

-bg color - sets the background color
-fg color - sets the foreground color

You can get a list of color names by looking at /usr/X11R6/lib/X11/rgb.txt. Try creating some applications with different colors, for example:

xman -bg red -fg white

You can specify a size and location for an X application through command line arguments. When a location is specified, you do not need to place the window yourself. This is done by specifying the "geometry" of the window. The geometry consists of four numbers: window width, window height, distance from side, distance from top/bottom. Most applications measure width and height in pixels. Distance can be either a positive or negative value. A positive distance measures the distance from the left side of the screen to the left edge of the window, or the distance from the top of the screen to the top of the window. A negative distance measures the distance from the right edge of the screen to the right edge of the window, or the distance from the bottom edge of the screen to the bottom edge of the window. These numbers are put together with the following syntax:

-geometry <width>x<height>+x+y

For negative offsets, you would use - instead of +. For example,

xclock -geometry 300x100-1+1

will create a wide but short clock in the lower left corner of your screen.

Startup Applications

Whenever you login to an X terminal (on the console, not with eXceed from a Mac or PC), a file called .xinitrc is automatically executed. This starts some applications for you. For now, .xinitrc is a symbolic link to the standard setup for students. The file contains a shell script. This script is pretty simple and mostly just starts the X applications that you see when you log in. To understand what it is doing, you first should know that you are not running OpenWindows when you log in. As a result, you execute the else part of the script. Among other things, this script contains the following lines:

xterm -C -geometry 80x40+270+120 -sb -sl 1024 &
emacs -geometry 80x40+10+150 &
xbiff -geometry 67x64-70+100 -bg lightskyblue &
xclock -geometry 69x64-0+100 -bg lightskyblue &
xman -geometry 139x66-1+1 &

These are the applications that you see when you start up X in the Unix lab. xterm is the shell window. xbiff is the mailbox. If you want to start additional programs when you start X, put them in .local_xinitrc.

Another customization that people often do is to customize the background. You can choose a color for the background or an image file. To choose a color use xsetroot:

xsetroot -solid <color>

To choose an image, use xsetbg:

xsetbg -fullscreen <image file>

xsetbg understands common image formats such as jpeg and gif files.


Often, you will find some settings for an X application and you will want to always use those settings. It is annoying to need to specify them on the command line as options every time. To avoid doing this, you can declare those options in a file called .Xdefaults. The declarations in .Xdefaults have the following syntax:

<application>*<resource>:   value

You might decide that you always want xterms to have a blue background and a white font. You might also decide that they should be 30 rows tall and 80 characters wide. You could specify this in your .Xdefaults as:

xterm*background:  blue
xterm*foreground:  white
xterm*geometry:  30x80

After changing .Xdefaults, you need to tell X to use the new values using xrdb:

xrdb .Xdefaults

Note that while .Xdefaults is a symbolic link, there is no way to tell it to also load an additional file. So, if you want to change the customizations in .Xdefaults, you need to remove your copy of the file and create a new .Xdefaults. Of course, you could start with a copy of the standard version.

Window Managers

To change the general look-and-feel of X, you customize (or replace) your window manager. The window manager controls such things as what appears in the title bar, what happens when you iconify a window, what commands are in the menus, etc. You can run a completely different window manager. That is what happens if you select something from the Options menu before logging in that is other than Williams CS standard. In that case, you are still running Unix and X but with a window manager.

The window manager we are running is twm. To customize twm, you would modify the .twmrc file. If you look in .twmrc, you will see the following things:

The syntax for many of these is pretty easy to mimic. To get a full understanding of what is there, look at the twm man page.