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.
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.
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.
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.
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
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"
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
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