Bash scripting tutorial for beginners
In this article we will learn about different syntax in bash shell scripting. It is designed as descriptive so that it will be easy to understand.
Whitespace and Linebreaks
bash shell scripts are very sensitive to whitespace and linebreaks. Because the “keywords” of this programming language are actually commands evaluated by the shell, you need to separate arguments with whitespace. Likewise, a linebreak in the middle of a command will mislead the shell into thinking the command is incomplete. Follow the conventions we present here and you should be fine.
Variables
$ MYVAR=6 $ echo $MYVAR 6
All values held in variables are strings, but if they are numeric the shell will treat them as numbers when appropriate.
$ NUMBER="10" $ expr $NUMBER + 5 15
When you refer to a variable’s value in a shell script, it’s a good idea to surround it with double quotes to prevent certain runtime errors. An undefined variable, or a variable with spaces in its value, will evaluate to something unexpected if not surrounded by quotes, causing your script to malfunction.
$ FILENAME="My Document" Space in the name $ ls $FILENAME Try to list it
ls: My: No such file or directory --- Oops! ls saw 2 arguments ls: Document: No such file or directory $ ls -l "$FILENAME" -------- List it properly My Document ---------------- ls saw only 1 argument
If a variable name is evaluated adjacent to another string, surround it with curly braces to prevent unexpected behavior:
$ HAT="fedora" $ echo "The plural of $HAT is $HATs" The plural of fedora is ---------------------- Oops! No variable “HATs” $ echo "The plural of $HAT is ${HAT}s" The plural of fedora is fedoras ---------- What we wanted
Input and Output
Script output is provided by the echo and printf commands, which we described in “Screen Output” :
$ echo "Hello world" Hello world $ printf "I am %d years old\n" `expr 20 + 20` I am 40 years old
Input is provided by the read command, which reads one line from standard input and stores it in a variable:
$ read name Sandy Smith <ENTER> $ echo "I read the name $name" I read the name Sandy Smith
Booleans and Return Codes
Before we can describe conditionals and loops, we need the concept of a Boolean (true/false) test. To the shell, the value 0 means true or success, and anything else means false or failure.
Additionally, every Linux command returns an integer value, called a return code or exit status, to the shell when the command exits. You can see this value in the special variable $?:
$ cat myfile My name is Sandy Smith and I really like Fedora Linux
$ grep Smith myfile
My name is Sandy Smith and --------------- A match was found...
$ echo $? 0 -----------------------------------------...so return code is “success” $ grep aardvark myfile $ echo $? ------------------------------------No match was found... 1 ---------------------------------------------...so return code is “failure”
test and “[”
The test command (built into the shell) will evaluate simple Boolean expressions involving numbers and strings, setting its exit status to 0 (true) or 1 (false):
$ test 10 -lt 5 -------------------------Is 10 less than 5? $ echo $? 1 ----------------------------------------No, it isn't $ test -n "hello" -------------------------Does the string “hello” have nonzero length? $ echo $? 0 ------------------------------------------Yes, it does
A list of common test arguments are found in Table 12, for checking properties of integers, strings, and files.
test has an unusual alias, “[” (left square bracket), as a shorthand for use with conditionals and loops. If you use this shorthand, you must supply a final argument of “]” (right square bracket) to signify the end of the test. The following tests are identical to those before:
$ [ 10 -lt 5 ] $ echo $? 1 $ [ -n "hello" ] $ echo $? 0
Remember that “[” is a command like any other, so it is followed by individual arguments separated by whitespace. So if you mistakenly forget some whitespace:
$ [ 5 -lt 4] ----------------------------------No space between 4 and ] bash: [: missing ']'
then test thinks the final argument is the string “4]” and complains that the final bracket is missing.
------------------------------------------- File tests ------------------------------------------- -d name File name is a directory -f name File name is a regular file -L name File name is a symbolic link -r name File name exists and is readable -w name File name exists and is writable -x name File name exists and is executable -s name File name exists and its size is nonzero f1 -nt f2 File f1 is newer than file f2 f1 -ot f2 File f1 is older than file f2 ------------------------------------------- String tests --------------------------------------------- s1 = s2 String s1 equals string s2 s1 != s2 String s1 does not equal string s2 -z s1 String s1 has zero length -n s1 String s1 has nonzero length ----------------------------------------------- Numeric tests ----------------------------------------------- a -eq b Integers a and b are equal a -ne b Integers a and b are not equal a -gt b Integer a is greater than integer b a -ge b Integer a is greater than or equal to integer b a -lt b Integer a is less than integer b a -le b Integer a is less than or equal to integer b ---------------------------------------------------------- Combining and negating tests ----------------------------------------------------------- t1 -a t1 And: Both tests t1 and t2 are true t1 -o t2 Or: Either test t1 or t2 is true ! your_test Negate the test, i.e., your_test is false \( your_test \) Parentheses are used for grouping, as in algebra
true and false
————————
bash has built-in commands true and false, which simply set their exit status to 0 and 1, respectively.
$ true $ echo $? 0 $ false $ echo $? 1
These will be useful when we discuss conditionals and loops.
Conditionals
————————-
The if statement chooses between alternatives, each of which may have a complex test. The simplest form is the if-then statement:
if command ------------------------------------------- If exit status of command is 0 then body fi
For example: if [ `whoami` = "root" ] then echo "You are the superuser" fi
Next is the if-then-else statement:
if command then body1 else body2 fi
For example: if [ `whoami` = "root" ] then echo "You are the superuser" else echo "You are an ordinary dude" fi
Finally, we have the form if-then-elif-else, which may have as many tests as you like:
if command1 then body1 elif command2 then body2 elif ... ... else bodyN fi
For example: ------------- if [ `whoami` = "root" ] then echo "You are the superuser" elif [ "$USER" = "root" ] then echo "You might be the superuser" elif [ "$bribe" -gt 10000 ] then echo "You can pay to be the superuser" else echo "You are still an ordinary dude" fi
The case statement evaluates a single value and branches to an appropriate piece of code:
echo 'What would you like to do?' read answer case "$answer" in eat) echo "OK, have a hamburger" ;; sleep) echo "Good night then" ;; *) echo "I'm not sure what you want to do" echo "I guess I'll see you tomorrow" ;; esac
The general form is:
case string in expr1) body1 ;; expr2) body2 ;; ... exprN) bodyN ;; *) bodyelse ;; esac
where string is any value, usually a variable value like $myvar, and expr1 through exprN are patterns (run the command info bash reserved case for details), with the final * like a final “else.” Each set of commands must be terminated by ;; (as shown):
case $letter in X) echo "$letter is an X" ;; [aeiou]) echo "$letter is a vowel" ;; [0-9]) echo "$letter is a digit, silly" ;; *) echo "I cannot handle that" ;; esac
Loops
———————————-
The while loop repeats a set of commands as long as a condition is true.
while command ----------------------------------------------- While the exit status of command is 0 do body done
For example, if this is the script myscript:
i=0 while [ $i -lt 3 ] do echo "$i" i=`expr $i + 1` done $ ./myscript 0 1 2
The until loop repeats until a condition becomes true:
until command ------------------------------------------ While the exit status of command is nonzero do body done For example: i=0 until [ $i -ge 3 ] do echo "$i" i=`expr $i + 1` done $ ./myscript 0 1 2
The for loop iterates over values from a list:
for variable in list do body done For example: for name in Tom Jack Harry do echo "$name is my friend" done
$ ./myscript Tom is my friend Jack is my friend Harry is my friend
The for loop is particularly handy for processing lists of files, for example, all files of a certain type in the current directory:
for file in *.doc do echo "$file is a stinky Microsoft Word file" done
For an infinite loop, use while with the condition true, or until with the condition false:
while true do echo "forever" done until false do echo "forever again" done
Presumably you would use break or exit to terminate these loops based on some condition.
Break and Continue
—————————————————————-
The break command jumps out of the nearest enclosing loop. Consider this simple script called myscript:
for name in Tom Jack Harry do echo $name echo "again" done echo "all done"
$ ./myscript Tom again Jack again Harry again all done
Now with a break:
for name in Tom Jack Harry do echo $name if [ "$name" = "Jack" ] then break fi echo "again" done echo "all done" $ ./myscript Tom again Jack ------------------------------- The break occurs all done
The continue command forces a loop to jump to its next iteration.
for name in Tom Jack Harry do echo $name if [ "$name" = "Jack" ] then continue fi echo "again" done echo "all done"
$ ./myscript Tom again Jack ---------------------------------- The continue occurs Harry again all done
break and continue also accept a numeric argument (break N,continue N) to control multiple layers of loops (e.g., jump out of N layers of loops), but this kind of scripting leads to spaghetti code and we don’t recommend it.
Creating and Running Shell Scripts
———————————————————————-
To create a shell script, simply put bash commands into a file as you would type them. To run the script, you have three choices:
Prepend #!/bin/bash and make the file executable This is the most common way to run scripts. Add the line:
#!/bin/bash
to the very top of the script file. It must be the first line of the file, left-justified. Then make the file executable:
$ chmod +x myscript
Optionally, move it into a directory in your search path. Then run it like any other command:
$ myscript
If the script is in your current directory, but the current directory “.” is not in your search path, you’ll need to prepend “./” so the shell finds the script:
$ ./myscript
The current directory is generally not in your search path for security reasons.
Pass to bash
bash will interpret its argument as the name of a script and run it.
$ bash myscript
Run in current shell with “.”
The preceding methods run your script as an independent entity that has no effect on your current shell.shell (setting variables, changing directory, and so on), it can be run in the current shell with the “.” command:
$ . myscript
* Technically, it runs in a separate shell (a subshell or child shell) that inherits the attributes of the original shell, but cannot alter them in the original shell.
Command-Line Arguments
————————————————————–
Shell scripts can accept command-line arguments and options just like other Linux commands. (In fact, some common Linux commands are scripts.) Within your shell script, you can refer to these arguments as $1, $2, $3, and so on.
$ cat myscript
#!/bin/bash echo "My name is $1 and I come from $2"
$ ./myscript Johnson Wisconsin
My name is Johnson and I come from Wisconsin
$ ./myscript Bob
My name is Bob and I come from Your script can test the number of arguments it received with $#:
if [ $# -lt 2 ] then echo "$0 error: you must supply two arguments" else echo "My name is $1 and I come from $2" fi
The special value $0 contains the name of the script, and is handy for usage and error messages:
$ ./myscript Bob ./myscript error: you must supply two arguments
To iterate over all command-line arguments, use a for loop with the special variable $@, which holds all arguments:
for arg in $@ do echo "I found the argument $arg" done
Exiting with a Return Code
————————————————————-
The exit command terminates your script and passes a given return code to the shell. By tradition, scripts should return 0 for success and 1 (or other nonzero value) on failure. If your script doesn’t call exit, the return code is automatically 0.
if [ $# -lt 2 ] then echo "Error: you must supply two arguments" exit 1 else echo "My name is $1 and I come from $2" fi exit 0
$ ./myscript Bob ./myscript error: you must supply two arguments $ echo $? 1
In case of any ©Copyright or missing credits issue please check CopyRights page for faster resolutions.