Programming tips and some time saving features of the bash shell

Lab 4
CSCI 112


Goals:
Learn some programming tips that will make implementing the programs easier.

Learn some simple features of the bash shell that will save you lots of time typing


Preventing files being included multiple times

Consider the following files:

main.cpp              
#include "bar.h"
#include "foo.h"


bar.h
#include "foo.h"

foo.h
class foo {...};

The preprocessor replaces each #include with the contents of the file being included.  In this example, when the file main.cpp is preprocessed, the contents of foo.h will replace the #include "foo.h" in both main.cpp and bar.h.  And the #include "bar.h" in main.cpp will be replaced by the contents of foo.h.  The files generated by the preprocessor will look like this:

bar.h
class foo {...}; // this came from replacing the #include "foo.h" with the contents of foo.h

main.cpp
class foo {...};  // this came from expanding the #include "bar.h"
class foo{...};  // this came from expanding the #include "foo.h"

Now when main.cpp is compiled, there will be a compilation error because class foo is multiply defined.  The problem is that foo.h was included multiple times.

You can prevent this problem by putting the following in EVERY .h file you write (substitute the filename of the current file for FOO):

#ifndef FOO_H   // this must go on the first line of your file
#define FOO_H

contents of file

#endif  // this must go on the last line of your file

How this works.

Many students have a hard time understanding how the above mechanism works, and since it can be used without understanding how it works, I'm not going to spend time lecturing on how it works.  I provide a brief description here for those of you who are sill curious, and I will be happy to discuss how it works anyone who is interested.

The directive ifndef stands for "if not defined."  We can read the above directives like this:

if the variable FOO_H is not defined                                      this is the #ifndef FOO_H
{
define the variable FOO_h                                            this is the #define FOO_H

rest of file goes here


}                                                                                            this is the #endif


When the preprocessor starts to process a new .cpp file, there are no variables defined (its table of defined variables is empty).  Each time it encounters a #define directive, it put that variable in its table.

The first time a .h file is read, the variable (by convention we use FILENAME_H, for the file filename.h) associated with that file is not in the table of defined variables, and thus the preprocessor enters the body of the if-not-defined statement and reads everything in the file.

The next time that .h file is read, the variable associated with that file IS in the table of defined variables and thus the preprocessor goes to the end of the if-not-defined statement (that is, the #endif) without reading everything in between.

The result is that the preprocessor will only ever read each .h file once and you won't get multiply defined errors.



Incremental Programming

When learning to program in a new language, the compiler can provide the most help if you write your programs incrementally.  In other words, write a few lines of code and then compile, and make sure your program works.  If you have only written a few lines of code, it will be much easier to figure out the errors the compiler generated, and it will much easier to track down logic errors.

Here is a game plan for writing program 2 incrementally:

  1. Create a makefile as describe in lab 3 or copy my Makefile.
  2. Create the Movie_db class files (movie_db.h and movie_db.cpp).  Make movie_db.cpp empty, and put an empty class declaration in movie_db.h.
  3. Add the code described above to prevent multiple includes.
  4. Put in all the #include directives (movie_db.cpp and main.cpp need to include movie_db.h)
  5. Write a simple dummy main() function (I usually just have my dummy main print out "hello world")
  6. Compile all 3 files.  Fix all errors until your program prints out "hello world."
  7. Write the code in main.cpp to read in the data.  Write temporary code to write out the data so you can be sure your program reads the data correctly.
  8. Compile and test your program with several different data sets, fix all problems now.
  9. Write, compile, and test class Movie_db 
  10. Add code to main() to call Movie_db's functions.
  11. You should be done at this point.

Programming incrementally will save you lots and lots of time even though you have to write some code you eventually will delete (e.g. printing hello words, and printing out the input from main



Making bash your default shell

On the department's computers, bash is not the default shell.

The choice of shells is a matter of personal preference.  bash is currently one of the most popular shells, and is available on many platforms including Windows (most varieties), Linux (all varieties), UNIX (all varieties), OSX.

If you make bash your default shell, you can use the features described below.  Chances are that bash will work just like your current shell, and you won't notice any difference, except that bash allows you to use the features described below.

To make bash your default shell:

Log on to cougar using a secure-shell (ssh)  (note: ssh is similar to telnet but encrypts your password so no one can steal it)

    $ ssh cougar.ecst.csuchico.edu

Now execute the command chsh (change shell)

    $ chsh /bin/bash

It will ask you for your password, and then change your shell.  After about an hour, every time you log onto a department machine, your default shell will be bash.

This mechanism is broken.  In order to make bash your default shell you either have to go to Elbert Chan's office hours or send him e-mail.

If you send him e-mail make sure you tell him your ECST username.

In the mean time, you can start the bash shell manually by typing:

$ /usr/bin/bash




bash profile

When bash starts, it executes the file .bash_profile in your home directory (unless something is screwed up with your preferences, see below).

You can customize bash so it behaves the way you like it by putting commands in .bash_profile.  There is an example of such a command below in the section of command line editing.

Another thing you can add to .bash_profile is called an alias.  An alias is another name for a command.  For example, if your 112 directory is:

    ~/classes/s06/112

to go to this directory you would have to type:

    cd ~/classes/s06/112

if you do this a lot, you can create an alias in .bash_profile that looks like this

    alias 112='cd ~/classes/s06/112'

now when you type 112 at the command prompt you will go to your 112 directory.  I set up dozens of alias to make it easy to navigate my file structure.

If your .bash_profile is not being executed automatically, the problem is probably with your windowing system settings.  You can manually run your .bash_profile by typing

    $ source .bash_profile



Command history

bash keeps track of all the commands you type in.  You can see a list of your recent commands using the history command

    $ history

each command has a number:

...
401  cd lab6_files
402 ls -l
403 pico hello.cpp
...

    you can execute one of the commands again by typing ! followed by the number.  For example,

$ !402

    would be the same as typing "ls -l"


Command line editing

Commands in the command history can also be edited

Use the up/down arrows to move up and down through the list, use the left/right arrows to edit a command

For example, assume that the last command was "pico hello.cpp" if you press up-arrow, "pico hello.cpp" will appear on the command line.  You can edit this to be "pico hello.h" by deleting the cpp and inserting an h

If you are a vi or vim user, you can set the mode of command line editing to use all the vi edit keys (e.g. i for insert, k for up).  Put the following in your .bash_profile (ONLY DO THIS IF YOU ARE A VI USER)

    set -o vi

There is also an emacs command line edit mode (ONLY DO THIS IF YOU ARE AN EMACS USER)

   set -o emacs


File completion

bash has a feature to save typing filenames.  When you press the <tab> key, bash matches what you have typed to all the files in the currently directory.  For example:

    $ pico c<tab>

will automatically fill in

    $ pico chart.cpp

IF chart.cpp is the only file in that directory that starts with c.  Assume there is another file that starts with c:  chart.h.  then we would get

    $ pico chart.

and you would have to fill in the "h" or type "c<tab>" to get the .cpp.

This is a real time savor, it is worth learning how to use.


More about shells

The bash shell (and other shells) can do a whole lot more.  There is an entire programming language at your finger tips.  I recommend you buy the O'Reilly book, Learning the bash shell to learn more about bash.

Note: this book is also available on-line through the CSU Chico Library:  Learning Bash  (NOTE these links don't always work, you might have to start at the Safari search page)


Exercise 1:
Try the above bash features.


Exercise 2:
Work on program 2