Previous Section  < Day Day Up >  Next Section

15.3. Types of Errors

There are several types of errors you will experience in shell programs: runtime errors, logical errors, and errors that could have been prevented by making the program robust. Runtime errors occur when the script starts running and encounters the use of a bad script name, permission issues, path problems, or syntactical errors such as mismatched quotes or mispelled words. The shell will report some diagnostic message if you have this type of error and normally abort the program. Logical errors are are often harder to find, because they are not obvious, and involve problems in the way in which the program was constructed, such as using the wrong operator in an expression, not knowing the exit status of a command, or using faulty logic in branching or looping statements. Other errors involve lack of robustness, that is, errors that could have been avoided with proper error checking, such as not anticipating bad user input or insufficient arguments, and so forth.

15.3.1 Runtime Errors

Runtime errors occur when the script starts running and encounters use of a bad script name, permission issues, path problems, or syntactical errors, such as mismatched quotes or mispelled words.

15.3.2 Naming Conventions

When you type a command at the shell prompt, the shell searches the UNIX/Linux path to find that command. When you type your script name at the prompt, it is treated as any other command—the shell searches the path to find it. But suppose you name a script with the same name as some other UNIX command. Suppose, for example, you name your program "ls". Which ls will be executed? It depends on which ls is found in the path first, and most likely the UNIX command will be executed instead of your script. Although it seems obvious to avoid calling your script ls or cat, it is less obvious with more obscure commands such as test or script. For example, because the UNIX test command produces no output, realizing that a naming problem exists can be tricky. The UNIX script command starts up another interactive shell, which copies your session at the terminal into a typescript file. It is not obvious that another shell has started running and may take some time before you realize something is really wrong. So avoid using test and script as filenames just as you would avoid using cat or ls.

One way to see if you have named a script after a UNIX/Linux command is to use the which command. It will show you the path where the named program is found. See Example 15.1. You can also start the script with a leading ./; then the script will run in your current working directory.

Example 15.1.

1   $ cat test

    #! /bin/sh

    echo "She sells C shells on the C shore"

2   $ test

    $ (no output)

3   $ echo $PATH

    /usr/bin:/bin:/usr/local/bin:/usr/X11R6/bin:/home/ellie/bin:.

4   $ which test

    /usr/bin/test

5   $ mv test mytest

6   $ mytest

    She sells C shells on the C shore


EXPLANATION

1 Notice that the script name is test, which is also a UNIX/Linux command name.

2 When the user tries to run the script, called test, the UNIX/Linux command runs instead. The UNIX test command produces no output.

3 The user's search path is displayed. The first path that will be searched is /usr/bin. The dot at the end of the path represents the user's current working directory.

4 The which command locates the test command and displays its full pathname. The UNIX/Linux test command is in /usr/bin/test.

5, 6 If the script called test is given a unique name, and not a UNIX/Linux command name, it should run as expected. The test script is renamed mytest.

15.3.3 Insufficient Permissions

Ordinary files do not have execute permission. If a script is run directly from the command line, it must have execute permission turned on, or the shell will tell you that you have insufficient permission. Scripts are made executable with the chmod command.

Example 15.2.

1   $ mytest

    sh: ./mytest: Permission denied.

2   $ ls -l mytest

    -rw-rw-r--  1   ellie     ellie     23 Jan  22   12:37    mytest

3   $ chmod +x mytest    # or chmod 755 mytest

4   $ ls -l mytest

    -rwxrwxr-x  1   ellie     ellie     23 Jan  22   12:37    mytest

5   $ mytest

    She sells C shells on the C shore


EXPLANATION

  1. When the script is executed, an error message is displayed indicating that the script does not have the proper permission to run.

  2. The ls –l command shows that the script does not have execute permission for anyone, including the owner of the script.

  3. The chmod command adds execute permission (+x) for everyone. Now the script should run.

  4. The ls –l command displays the new permissions. Execute permission is turned on.

  5. The script executes as expected.

15.3.4 Path Problems

As we mentioned before, when you type the name of a shell script on the command line, the shell will check the directories listed in the search PATH variable to locate your script. If the dot directory is listed in the path, the shell will look in the current working directory for your script. If the script is there, it will be executed. However, if the dot is not in your path, you will receive an error message: Command not found. To enable the shell to locate your script, you can add the dot (.) directory to your PATH. However, if you have a superuser account (an account with UID of 0), it is recommended, for security reasons, that you do not include a dot in the search path. There are two alternative solutions if you choose not to put the dot in your PATH variable:

  1. Explicitly indicate the location of your script by preceding the script name with ./ (e.g., ./mytest).

  2. Precede the script name with the name of the invoking shell (e.g., csh mytest or bash mytest).

When the script name is given as an argument to the shell, the shell automatically checks the current directory for the script name.

Example 15.3.

1   $ cat mytest

    #!/bin/sh

    echo "Users may wish to include the . directory in their PATH"

2   $ echo $PATH

    /usr/bin:/bin:/usr/local/bin:/usr/X11R6/bin:/home/ellie/bin:

3   $ mytest

    not found

4   $ ./mytest     # this is one possible solution

    Users may wish to include the . directory in their PATH

5   $ PATH=$PATH:. # this solution should only be used by non-root

    $ export PATH  # bash, sh, ksh set PATH this way

    % setenv PATH ${PATH}:.   # csh/tcsh set path this way

6   $ mytest

    Users may wish to include the . directory in their PATH


EXPLANATION

  1. The Bourne shell script is displayed. It is located in the user's current working directory.

  2. The PATH variable does not contain a dot, which represents the current working directory.

  3. The shell cannot locate the mytest script.

  4. The shell is now able to locate the script because the directory (the .) is included with the script name.

  5. The PATH is updated to include the current directory. The first syntax can be used for sh/ksh/bash shells, the second for csh/tcsh.

  6. The script now runs correctly.

15.3.5 The Shbang Line

When you create a script, the first line of the script is normally the shbang (#!) line. When the script is started, the UNIX kernel examines the first line of the file to determine what type of program is to be executed. The path that follows the shbang notation (#!) is the location of the shell that will be invoked to interpret this script. The #! characters must be the first two characters in the file for this feature to operate properly. If you have a blank line or blank space at the top of your file, this feature will be ignored and the line will be interpreted as an ordinary comment line.

Example 15.4.

(The Script)

1

2   #!/bin/csh

    # Scriptname: shbang.test

3   setenv MYVAR 18

    echo "The value of MYVAR is $MYVAR



(The Output)

    ./shbang.test: line 3: setenv: command not found

    The value of MYVAR is



(The Script)

4   #!/bin/csh

    setenv MYVAR 18

    echo "The value of MYVAR is $MYVAR



(The Output)

    The value of MYVAR is 18


EXPLANATION

1 Notice that this script has a blank line at the top. Also note that the script was written for the C shell. The setenv command on line 3 will not work in the Bourne, Korn, or Bash shells.

2, 3 Because the #! line is not the first line of the script, the script will be executed by the user's login shell. If the login shell is ksh, the Korn shell will execute the script; if bash, the Bash shell will execute the script. If, however, the user's login shell is C shell, this script will be executed by the Bourne shell. The error message is displayed because the Bourne shell doesn't understand the setenv command.

4 The script now runs correctly because the #! notation has been entered on the top line. The kernel will recognize the #! and start up the shell listed in the path that follows. Because the shell is /bin/csh, the C shell will be the interpreter for this script (provided that the execute permissions are turned on). The setenv command is understood by the C shell.

Example 15.5.

1   #! /bin/chs

    echo "Watch out for typing errors on the #! line"

2   $ ./mytest

    mytest: Command not found.



----------------------------------------



3   #! /bin/csh

    echo "Watch out for typing errors on the #! line"

4   ./mytest

    Watch out for typing errors on the #! line


EXPLANATION

  1. The shbang line has a typing error in the shell name. The shell should be /bin/csh instead of /bin/chs. Misspelling on the shbang line is a very common error!

  2. Because of the typing error, the kernel looks for the /bin/chs program, causing the Command not found error message. This message can be misleading, causing you to think that the script itself or a command typed in the script cannot be found. If the only error you get is Command not found, first check the shbang line for spelling.

  3. The spelling of the shell is corrected on the shbang line.

  4. The script now runs correctly.

15.3.6 Sneaky Aliases

An alias is an abbreviation or an alternative name for a command. Aliases are not supported by the Bourne shell. Aliases are usually put in an initialization file and used as shortcuts at the command line, but are not readily used in shell scripts. However, they may find their way into a script if the user's .cshrc or .kshrc defines an alias and does not limit the aliases to interactive shell sessions.

Example 15.6.

(In the .cshrc File)

1   alias ls 'ls -aF'



(At the shell prompt)

2   $ ls

    ./        a         c         t1        t3

    ../       b         t*        t2        tester*



(The Script)

    #!/bin/csh

3   foreach i ( `ls` )

4       echo $i

    end



(Output)

 ./

../

a

b

c

t t1 t2 t3 tester   <-- What happened here?

t1

t2

t3

tester



----------------Possible Fix--------------------------



5   #!/bin/csh -f      <-- Add the -f option to the shbang line

6   unalias ls         <-- turn off the alias


EXPLANATION

  1. In the user's .cshrc initialization file, an alias has been defined. Normally the .cshrc file is started up any time you invoke a C shell, including a C shell script. Many generic .cshrc files define aliases after the expression:

    
    if ( $?prompt ) then
    
       < aliases listed here >
    
    endif
    
    

    This means: If you are running an interactive shell, that is, a shell that produces a prompt, then you can use the aliases. Because shell scripts are noninteractive and do not have a prompt, they would not able to use the aliases. (Often aliases are also stored in a file called .aliases and are sourced in the .cshrc file, .kshrc file, .bashrc file, or .tcshrc file.)

  2. When the user types ls at the prompt, the alias is used. It causes ls –aF to be executed. The –F switch lists files with a * appended to executable files, a / appended to directories, and an @ appended to symbolic links. The –a switch causes invisible files to be printed (i.e., those files starting with a period).

  3. In the shell script, the ls command is executed in the foreach expression. The shell will evaluate the alias, causing the ls –aF command to list the files in the format described above, something the unsuspecting programmer may not realize. Each executable file is appended with a *, which the shell will treat as a globbing metacharacter and start matching on filenames. Because the file called t is an executable file, it will be listed as t*, causing the shell to match all files starting with a t.

  4. All the files are listed using the alias.

  5. One solution to this problem is to add the –f option to /bin/csh in the shbang line. Often called a fast startup, the –f switch tells the shell not to load the .cshrc file when it starts the script. For ksh the shbang line would be #!/bin/ksh –p and for bash it would be #!/bin/bash – –noprofile.

    Previous Section  < Day Day Up >  Next Section