BASH Scripting bash is great for simple scripts that automate things you would otherwise by typing on the command line. Your command line skills will carry over to bash scripting and vice versa. bash comments start with a hast mark, i.e. #, and continue to the end of the line. As on the command line you can break a single logical line onto multiple physical lines by escaping the newline with a backslash, i.e \. You can also put more than one statement on a line by separating the statements with semicolons. A bash script may consist of nothing but a series of command lines, The following helloworld.sh script simply es an echo. echo Hello, world! The rst line is known as the shebang statement and declares the text le to be a script for interpretation by /bin/bash. The kernel looks for this syntax when deciding how to execute the le. From the perspective of the shell, the shebang line is just a comment. To prepare the le for execution, you need to turn on the execute bit. There are several different ways of ing this. Perhaps the easiest is $ chmod +x helloworld.sh $./helloworld.sh Hello, world! The rst line sets the execution bits and the second line executes the shell script. Note: The -x option to bash prints each command before executing it. /bin/bash -x helloworld.sh + echo 'Hello, world!' Hello, world! Input and Output The echo command is crude but easy. For more control over your output, use printf. It is a little less convenient because you must explicitly put newlines where you want them, \n, but it gives you the option to use tabs and enhanced number formatting in your output. Compare the output from the following two commands: $ echo \taa\tbb\tcc\n \taa\tbb\tcc
$ printf \taa\tbb\tcc\n aa bb cc You can use the read command to prompt for input. Suppose that we have the following script: echo -n "Enter your name: " read user_name if [ -n "$user_name" ] echo "Hello $user_name!" exit 0 echo "You did not tell me your name!" exit 1 We execute the script getting $./readexample.sh Enter your name: Harold Hello Harold! $./readexample.sh Enter your name: You did not tell me your name! $ The -n in the echo command suppresses the usual newline, but you could also have used printf here. The -n in the if statement evaluates to true if its string argument is not null. Command-Line Arguments and Functions Command-line arguments to a script become variables whose names are numbers. $1 is the rst command-line argument, $2 is the second, etc. $0 is the name by which the script was invoked. The variable $# contains the number of command-line arguments that were supplied, and the variable $* contains all of the arguments at once. Neither of these variables includes or counts $0. If you call a script without arguments or with inappropriate arguments, the script should print a short usage message to remind the user how to use it. The example script below accepts two arguments, validates that the arguments are both directories, and displays them. If the arguments are invalid, the script prints a usage message and exits with a nonzero return code. If the caller of the script checks the return code, it will know that this script failed to execute correctly.
# functions function show_usage { echo "Usage: $0 source_directory destination_directory" exit 1 } # Main program starts here if [ $# -ne 2 ] show_usage # There are two arguments if [ -d $1 ] source_dir=$1 echo "Invalid source directory" show_usage if [ -d $2 ] dest_dir=$2 echo "Invalid destination directory" show_usage printf "Source directory is $source_dir\n" printf "Destination directory is $dest_dir\n" Executing this script yields $./example.sh /bin /usr/bin Source directory is /bin Destination directory is /usr/bin $./example.sh /bin Usage:./example.sh source_directory destination_directory $ echo $? 1 $ Note: The shell variable $? contains the exit status of the last command executed.
Control Flow If We have already seen several if- and if-- statements already. They exactly what you would expect. The terminator for an if statement is. You can also chain your if clauses together using the elif keyword to mean if. if [ $base eq 1 ] && [ $dm eq 1 ] installdmbase elif [ $base ne 1 ] && [ $dm eq 1 ] installbase elif [ $base eq 1 ] && [ $dm ne 1 ] installdm echo ==> Installing nothing Both the peculiar [ ] syntax for comparisons and the command-line option like names of the integer comparison, eq, are inherited from the Bourne shell. The table below shows the bash comparison operators for numbers and strings. bash uses textual operators for numbers and symbolic operators for strings. String Numeric True if x = y x -eq y x is equal to y x!= y x -ne y x is not equal to y x < y x -lt y x is less than y x <= y x -le y x is less than or equal to y x > y x -gt y x is greater than y x >= y x -ge y x is greater than or equal to y -n x - x is not null -z x - x is null bash shines in its options for evaluating the properties of les. The table below shows a few of bash s many le testing and le comparison operators.
Operator True if -d le le exists and is a directory -e le le exists -f le le exists and is a regular le -r le You have read permission on le -s le le exists and is not empty -w le You have write permission on le le1 -nt le 2 le 1 is newer than le 2 le1 -ot le 2 le 1 is older than le 2 Loops bash s for in construct makes it easy to take some action for a group of values or les, especially when combined with lename globbing, i.e. the expansion of simple pattern-matching characters such as * and? to form lenames or list of le names. The general syntax for the for in statement is for <var> in <list of values> In the example below we want to backup all of our shell scripts to a le extension that indicates the backup time and date. The *.sh pattern in the for loop below returns a list of matching lenames in the current directory that end with the characters sh. The for statement iterates through that list and assigns each lename in turn to the variable $le. sufx=backup--`date +%Y%m%d-%H%M` for script in *sh newname= $script.$sufx echo Copying $script to $newmane cp $script $newname Executing the above script, we might have Copying example.sh to example.sh.backup 20120208-1058 Copying hcg.sh to hcg.sh.backup 20120208-1058
The lename expansion is not magic in this context. It works exactly as it es on the command line, i.e. the expansion happens rst and the line is processed by the interpreter in its expanded form. You could just as well have entered the lenames statically as in for script in example.sh hcg.sh Alternatively you could have used ` and the shell command ls for script in `ls *sh` Obviously, the rst approach is the best. bash also has the more familiar for loop from traditional programming languages in which you specify starting, increment, and termination clauses, for ((i=0; i<$end; i++)) Additionally, bash has the familiar while loop which can be useful for processing command line arguments or for reading the lines in a le. exec 0<$1 counter=1 while read line echo $counter: $line counter=$(($counter+$((1)))) This script has a couple of interesting features. The exec statement redenes the script s standard input to come from whatever le is named by the rst command line argument. The le must exist or the script generates an error. The read statement within the while clause is in fact a shell built-in, but it acts like an external command. When the end-of-le is read, the while loop terminates. The weird statement that increments the counter is explained in the next section. Arithmetic Calculations are not bash s forte. But we can arithmetic within a bash script. All bash variables are string values. Hence, bash es not distinguish between the number 1 and the character 1 in assignments. The difference lies in how the variables are used. The following illustrates this distinction:
a=1 b=$((2)) c=$a+$b d=$(($a+$b)) echo $a + $b -> $c \t(plus sign as string literal) echo $a + $b -> $d \t(plus sign as arithmetic addition) Executing the script we get 1 + 2 -> 1+2 \t(plus sign as string literal) 1 + 2 -> 3 \t(plus sign as arithmetic addition) Note: The plus sign in the assignment above es not even act as a concatenation operator for strings. It is just a literal character. The line is equivalent to C= $a+$b To force numeric evaluation, you enclose an expression in $(( )), as shown with the assignment to $d above. Even this precaution es not match the result in $d receiving a numeric value. The value is still stored as the string 3. bash has the usual assortment of arithmetic, logical, and relational operators.