Generalizing Your Code

All programs and scripts require input in order to be useful. Inputs commonly include things like scalar parameters to use in equations and the names of files containing more extensive data such as a matrix or a database.

Hard-coding: Failure to Generalize

All too often, inexperienced programmers provide what should be input to a program by hard-coding values and file names into their programs and scripts:

#!/bin/sh -e

# Hard-coded values 1000 and output.txt
calcpi 1000 > output.txt
            

Some programmers will even make another copy of the program or script with different constants or file names in order to do a different run. The problem with this approach should be obvious. It creates a mess of many nearly identical programs or scripts, all of which have to be maintained together. If a bug is found in one of them, then all of them have to be checked and corrected for the same error.

Generalizing with User Input

A more rational approach is to take these values as input:

#!/bin/sh -e

printf "How many iterations? "
read iterations
printf "Output file? "
read output_file

calcpi $iterations > $output_file
            

If you don't want to type in the values every time you run the script, you can put them in a separate input file, such as "input-1000.txt" and use redirection:

shell-prompt: cat input-1000.txt
1000
output-1000.txt
shell-prompt: calcpi-script < input-1000.txt
            

This way, if you have 10 different inputs to try, you have 10 input files and only one script to maintain instead of 10 scripts.

Generalizing with Command-line Arguments

Another approach is to design the script so that it can take command-line arguments, like most Unix commands. Using command-line arguments is quite simple in most scripting and programming languages. In all Unix shells, the first argument is denoted by the special variable $1, the second by $2, and so on.

$0 refers to the name of the command as it was invoked. A script file can be renamed, so hard-coding the current name in error messages is a bad idea. Using $0 eliminates future maintenance.

Bourne Shell Family

In Bourne Shell family shells, we can find out how many command-line arguments were given by examining the special shell variable $#. This is most often used to verify that the script was invoked with the correct number of arguments.

#!/bin/sh -e

# If invoked incorrectly, tell the user the correct way
if [ $# != 2 ]; then
    printf "Usage: $0 iterations output-file\n" >> /dev/stderr
    exit 1
fi

# Assign to named variables for readability
iterations=$1
output_file="$2"    # File name may contain white space!

calcpi $iterations > "$output_file"
                
shell-prompt: ./calcpi-script
Usage: calcpi-script iterations output-file
shell-prompt: ./calcpi-script 1000 output-1000.txt
shell-prompt: cat output-1000.txt
3.141723494
                
C shell Family

In C shell family shells, we can find out how many command-line arguments were given by examining the special shell variable $#argv.

#!/bin/csh -ef

# If invoked incorrectly, tell the user the correct way
if ( $#argv != 2 ) then
    printf "Usage: $0 iterations output-file\n" >> /dev/stderr
    exit 1
endif

# Assign to named variables for readability
set iterations=$1
set output_file="$2"    # File name may contain white space!

calcpi $iterations > "$output_file"
                
Practice

Note

Be sure to thoroughly review the instructions in Section 2, “Practice Problem Instructions” before doing the practice problems below.
  1. Modify the following shell script so that it takes the starting and ending values of the loop as user input rather than hard-coding them.

    #!/bin/sh -e
    
    for c in $(seq 1 10); do
        c_squared=$(($c * $c))
        printf "%s^2 = %s\n" $c $c_squared
    done
            
    shell-prompt: ./squares.sh
    First value to square? 3
    Last value to square? 9
    3^2 = 9
    4^2 = 16
    5^2 = 25
    6^2 = 36
    7^2 = 49
    8^2 = 64
    9^2 = 81
            
  2. Repeat the above exercise, but use command-line arguments instead of user input.

    shell-prompt: ./squares.sh
    Usage: ./squares.sh first-value last-value
    
    shell-prompt: ./squares.sh 4 10
    4^2 = 16
    5^2 = 25
    6^2 = 36
    7^2 = 49
    8^2 = 64
    9^2 = 81
    10^2 = 100