Introduction to Bash
- I. Introduction
- a. What is Bash?
b. Bash competitors
II. Using Bash
- a. Editing the command line
b. Controlling the command history
c. Wildcards
d. Aliases
e. I/O redirection
f. Pipelines
g. Shell prompts
h. The environment
III. Bash Scripts
- a. Built-in commands
b. External commands
c. Shell programming
I. Introduction
I.a. What is Bash?
Bash (the Bourne Again SHell) is a command shell. It provides a way for the user to communicate to the Linux kernel, and for the kernel to communicate back to the user. When you enter something at the command prompt, that information is (usually) passed along to the kernel. The results are (usually) passed back to the Bash shell.
Like the MSDOS/Windows program command.com, Bash is a command-language interpreter. Unlike command.com, it is a very powerful program – one that provides enough power and utility to actually write program applications in.
The Bash shell has its own built-in interpretive programming language, which supports most of the programming constructs found in high-level languages, such as looping, functions, variables, and arrays. It has a set of built-in commands such as cd, echo and pwd. It can execute other programs, such as ps, find and grep.
I.b. Bash competitors
Unlike MSDOS/Windows command.com, there is more than one command shell in Linux competing for your affections. Bash is probably the most popular, and is the default for most Linux distributions. Other shells available include the C-shell (csh), the Bourne Shell (bsh) and the Korn Shell (ksh).
All of these shells have their strengths and weaknesses. Bash seems to be the most well-rounded of the shells, providing strong input and processing capabilities.
II.a. Editing the command line
When you enter commands at the Bash prompt, you may often use command line completion to help you with the command. Here's how it works. Let's say you are user mburton and you are in a directory that contains a file called 'myverylongbashtalk.txt'. You want to copy that file to a directory called '/home/mburton/work'. You start entering the copy command like this:
cp myv
At this point, you don't have to type the rest of the file name – you can use command line completion to do that. Simply hit the Tab key, and if the first three letters you typed start only one file, that file will be displayed on the command line:
cp myverylongbashtalk.txt
If more than one file starts with those letters, Bash will beep at you and you can type a few more letters and press tab again.
To complete the command, we can type out the long path to the directory, or we can use a shortcut. In Bash, the tilde (~) means 'user home directory', so we can type
cp myverylongbashtalk.txt ~/work
instead of
cp myverylongbashtalk.txt /home/mburton/work
All of this built-in Bash capability is designed to make your life a bit easier.
If you need to edit a command because you've forgotten something or you made a mistake, you can do that using the backspace, delete, left and right arrow keys. Remember that Bash is always in 'insert' mode so if you type something in the middle of the command line, everything to the right is moved over to make room for the insertion.
Here is a list of some of Bash's command line editing keys:
| Key(s) | Command |
|---|---|
| Tab | Complete a file name if possible |
| Backspace | Erase the character to the left of the cursor |
| Delete | Erase the character under the cursor |
| Home, Ctrl-A | Go to the start of the line |
| End, Ctrl-E | Go to the end of the line |
| Left Arrow, Ctrl-B | Go one character to the left (back) |
| Right Arrow, Ctrl-F | Go one character to the right (forward) |
| Ctrl-K | Delete from cursor to end of line |
| Ctrl-U | Delete from cursor to start of line |
| Ctrl-W | Delete one word to the left |
| Ctrl-Y | Undelete what was just deleted |
| Ctrl-L | Clear screen |
| Enter, Ctrl-M | Execute the command line |
Here is a list of how you can use the tilde as a shortcut:
| Keys | Command |
|---|---|
| ~ | Substitute the contents of $HOME in the line |
| ~user | Substitute the user's home directory |
| ~+ | Substitute $PWD (current directory) |
| ~- | Substitute $OLDPWD (last current directory) |
II.b. Controlling the command history
The Bash command line maintains a limited history of the commands you have entered. It maintains a unique history for each user, so if you switch users using the 'su' command, you will have a different history to work with.
The Bash command history is provided to reduce the amount of work you have to do to create a command line. If you want to do the same or similar command repetitively, you can pick up the command by navigating through the command history, make your changes and execute the command. Executed commands will be added to the command history.
There are a couple of ways to navigate through the command history. The easiest method is to use the up arrow and down arrow keys to scroll in the history.
If your history is long and you want to pick up a particular command, try using Ctrl-R. You press the Ctrl-R key, then the first letter of the command you want. A command starting with that letter will be displayed. If it is not the correct command, press Ctrl-R again and the next command in the history that starts with that letter will be displayed. Keep pressing Ctrl-R until you find the command line you want.
Here is a summary of the command history keys:
| Key(s) | Command |
|---|---|
| Up arrow | Display the command line previous to the current one |
| Down arrow | Display the command line next from the current one |
| Ctrl-R (letter) | Display the command line starting with (letter) |
| Page Up | Go to the start of the history |
| Page Down | Go to the end of the history |
You can also access the command line history using two Bash commands. The 'history' command will display all lines of the history. If you add a number to the history command, it will display the latest number of lines to be displayed, i.e.,
history 5
will display the last 5 lines of history.
The other command that accesses the comand line history is the 'fc' command. fc allows you to read the history and put the results in an editor, where you can make changes to it. You can then use the history command to write the changed history to the actual history file.
II.c. Wildcards
Whenever you enter a command in Bash, you can use 'wildcards' in the command. A wildcard is a special character or characters that will match anything. Wildcard characters used by bash are the asterisk '*', the question mark '?' and square brackets. The asterisk will match zero or more characters. The question mark will match a single character. The square brackets will match a specific range of characters. For instance, suppose we are in a directory that contains the following files:
- file.txt
file1.txt
file2.txt
file3.TXT
txtfile
If we want to list all the files that start with the word 'file', we would enter
ls file*
The asterisk will match all the characters following the 'file' part, which will list all the files except 'txtfile'. If we want to list just the 'txtfile', we could enter
ls *file
If we want all the files that end in '.txt', we would enter
ls *.txt
Note that the 'file3.TXT' file would not match the above request, since its extension is in upper case.
If we want to list all files with 'file' in them, regardless of position in the name, we could enter
ls *file*
We can use the question mark to make more detailed matches. If we wanted all the files that have a number in them, we could use
ls file?.*
The first and last file would not be listed. We could also use square brackets instead of the question mark, and put the character range in that we need:
ls file[0-9].*
ls file[123].*
These will both match the same way as the question mark.
Special note: The MSDOS command.com shell also supports wildcard characters, but the two shells use them very differently. command.com passes along the wildcard to whatever command it is executing. It is up to that command to expand and use the wildcard character.
On the other hand, Bash expands the wildcard on the command line before passing along the command line to the command. This means that the command does not need to do any work to expand the wildcard. For example, in the above directory, if you wanted to list the files that ended in numbers using command.com, you would enter
dir file?.*
and it would pass the following to the dir built-in command
file?.*
In Bash, you would enter
ls file?.*
and it would pass the following to the ls built-in command
file1.txt file2.txt file3.TXT
II.d. Aliases
An alias is a short command that Bash can translate to another longer command. For instance, let's say that you prefer to use the 'ls' command with the '-la' flags. We can define an alias for that and call it 'dir':
alias dir="ls -la"
We can then use the 'dir' command in place of the 'ls -la' command.
You can also use alias to redefine existing commands. Many distributions do this. For instance, have you ever tried to remove files and have Bash ask you if you want to remove each and every file? The 'rm' command has been redefined like this:
alias rm="rm -i"
which puts it in interactive mode.
Any commands that are redefined have been done in the .bashrc script, which starts up when you start up a Bash shell. There is a .bashrc script for each user in their home directory.
II.e. I/O redirection
Redirection allows the user to change the source or destination of data being used by a Bash command.
Commands or programs sometimes get their input from the console and send their output to the console. The source of data from the console is a stream called 'stdin'. The destination for output is a stream called 'stdout' and the destination for error messages is a stream called 'stderr'. I/O redirection allows you to change these sources/destinations to be other streams. You can get input from a file and send output to another file or even to the null device. Here is a list of I/O redirection characters:
| Keys | Command |
|---|---|
| &>word | send both stdout and stderr to 'word' |
| >&word | send both stdout and stderr to 'word' |
[n]| use 'file' for input |
|
| [n]>file | use 'file' for output |
| [n]>!file | same as '>', but overrides noclobber. |
| [n]>>file | same as '>', but append data if file exists |
| [n]<>file | open file for read/write |
| [n]<&m | duplicate input file descriptor from m |
| [n]>&m | duplicate output file descriptor from m |
| [n]<&- | close input file descriptor |
| [n]>&- | close output file descriptor |
These redirection characters can be used on the command line, or in a script. The [n] allows you to open multiple streams simultaneously.
Example: list directory contents to a file named 'home.ls'.
ls * >home.ls
Example: run the 'foo' command, getting the input from file 'input.foo' and sending the output and error messages to the null device.
foo
/dev/null
or
foo
/dev/null 2>&1
II.f. Pipelines
Pipes allow you to feed the output of one command or program into another command or program as its input. The pipe character (|) is used to string these multiple commands together.
Example: Look for the string 'net' in the current programs that are running.
ps -ef | grep net
The output from the 'ps' command is fed to the 'grep' command. It then looks for a match to the string 'net'. If it finds one, that line is displayed.
II.g. Shell prompts
When you are interactively using Bash, the program prompts you for input by displaying a prompt. It does this by printing the primary prompt string $PS1. Whatever is stored in that string is used as the bash prompt. The prompt string has its own special characters that can be used to display some very informative prompts. The special characters are
| Keys | Command |
|---|---|
| \\a | the ASCII BEL character. Beeps at you. |
| \\d | the date in Weekday Month Day format |
| \\e | the ASCII escape character (octal 033) |
| \\h | the hostname up to the first dot (.) |
| \\H | the full hostname |
| \\n | the ASCII newline character |
| \\r | the ASCII carriage return character |
| \\s | the name of the shell |
| \\t | the time in 24-hour HH:MM:SS format |
| \\T | the time in 12-hour HH:MM:SS format |
| \\u | the user's username |
| \\v | the version of the bash shell |
| \\V | the version and patch level of the bash shell |
| \\w | the full path of the current working directory |
| \\W | the current working directory only |
| \\! | the history number of this command |
| \\# | the command number of this command |
| \\$ | a '#' if the effective UID is 0, otherwise a '$' |
| \\@ | the time in 12-hour am/pm format |
| \\\\ | the backslash character |
| \\nnn | the ASCII character corresponding to the octal number nnn |
| \\[ | start a sequence of non-printing characters |
| \\] | end a sequence of non-printing characters |
You can use the escape character with VT-100 escape sequences to create colorful and useful command prompts.
II.h. The environment
Like the command.com shell in MSDOS, Bash maintains an environment for each instance of itself. The environment contains shell settings in the form of environment variables. We have already touched on several of these ($HOME, $PWD, $PS1, etc.). Whenever a program is executed, that program receives a
copy of the environment variables from the process which executes it.
You can display a list of the current environment by typing 'set'.
You can create new environment variables or change the values of old variables. To do this, simply type the name of the variable, an equals sign, and the value of the variable.
These environment variables have many uses. You can display them, test them or programs you are running can use them.
III. Bash Scripts
Bash has a built-in language you can use to create complex actions on your computer. When you put these actions in a file to be executed by Bash, it is called a 'shell script'. A shell script is basically a small program that is interpreted and executed by Bash.
III.a. Built-in Commands
Bash supports two kinds of commands – those that are built into the Bash shell, and those that are separate executable programs or scripts.
Here is a list of the commands that are built into Bash:
| Command | Description |
|---|---|
| source | read and execute commands from a file |
| : | null command returning 0 exit status |
| alias | create an alias |
| bg | put a job in the background |
| bind | display/modify 'readline' function and key bindings |
| break | exit from an enclosing for, while, until or select loop |
| builtin | execute a builtin command and return status |
| cd | change current directory |
| cd - | change current directory to $OLDPWD |
| command | execute a command with arguments |
| continue | do next iteration of for, while, until or select loop |
| declare typeset |
set attributes and values of variables |
| dirs | display the directory stack |
| disown | remove jobs from the table of active jobs |
| echo | display the rest of the line on the stdout device |
| enable | enable/disable shell built-ins; load/unload new built-ins |
| eval | evalute the rest of the line and execute the result |
| exec | execute the rest of the line in place of the shell |
| exit | exit from a shell script with a return value |
| export | no args – print names and values of exported variables with args – export variable to the environment |
| fc | print range of history commands |
| fg | put a job into the foreground |
| getopts | parse parameters and options |
| hash | no args – print the hash table contents with args – add a name to the hash table |
| help | print help for commands that match the supplied pattern |
| history | print the command history |
| jobs | list information about jobs |
| kill | terminate the listed job |
| let | evaluate an expression. Exit 0 if non-zero, 1 otherwise |
| local | create variables local to the current function |
| logout | exit a login shell |
| popd | remove entries from the directory stack |
| printf | print output like ANSI C printf |
| pushd | add an entry to the directory stack |
| pwd | print working directory name |
| read | read stdin and assign values to a list of variables |
| readonly | mark variables as read only |
| return | exit a function with a return value |
| set | no args – display the environment variables with args – set flags and options |
| shift | rename positional parameters |
| shopt | print or change values of shell options |
| suspend | suspend current shell until a SIGCONT is received |
| [ test |
evaluate conditional expressions |
| times | print accumulated process times |
| trap | execute a command if a specific signal is received |
| type | describe how the shell interprets a name |
| ulimit | set or print per-process limits |
| umask | set the creation permissions |
| unalias | remove an alias |
| unset | remove a variable |
| wait | wait for a specific job. |
III.b. External commands
All commands in a shell script that are not on the above list should be external commands. If you specify a complete directory path to the command, Bash will use that path. If you specify just the command, Bash will look in your $PATH list for the command.
III.c. Shell programming
By using a combination of the above built-in commands, external commands and Bash control commands, you can write shell scripts that will perform complex functions. The control commands allow you to control the script execution flow. Here is a list of control commands for Bash:
| Command | Description |
|---|---|
| ! | execute a pipeline |
| case..in..esac | case statement for Bash |
| for..in..do..done | for statement for Bash |
| func() {..} | define a function |
| if..then..else..fi | if statement for Bash |
| select..in..do..done | menu statement for Bash |
| time | execute a pipeline; print times on stderr |
| until..do..done | until statement for Bash |
| while..do..done | while statement for Bash |
| ((..)) | arithmetic evaluation |
| [[..]] | expression evaluation |
| (..) | execute the list in a sub-shell |
| {..} | execute the list in the current shell |
The best way to descirbe how these commands are used is with examples. Here is a small script that prints the numbers from 0 to 9:
#!/bin/bash
# Print numbers from 0 up to 9
for i in 0 1 2 3 4 5 6 7 8 9; do
echo $i
done
Note that the first line starts with a shebang (#!). This tells the shell which command interpreter to use to run the script. It could easily be perl or C shell instead of bash, but our script is a bash script.
The next line is a comment so we will remember what the script does.
Next is a program loop. The variable i is assigned each of the values in the list. When the list is exhausted, the loop ends. Inside the loop we echo the variable i to the console.
Here is another example which uses an 'if' statement:
#!/bin/bash
# Make a backup of the fstab file, if it exists
if [[ -e /etc/fstab ]]; then
cp /etc/fstab /etc/fstab.bakup
echo "fstab backed up."
else
echo "fstab does not exist."
fi
As a final example, here is how to process command line arguments:
#!/bin/bash
# Check for a cmd line arg and echo it
if (( $# > 0 )); then
echo "Command line argument: $1";
else
echo "No command line argument present"
fi
Note that the command line arguments are numbered $0, $1, $2, etc. The $0 argument contains the name of the script being run, so the first argument is $1. The $# variable contains the number of command line arguments.
This concludes the introduction to bash. If you want to learn more about the command shell, I suggest that you look for a tutorial on the Internet, or purchase "Learning the bash Shell", Second Edition By Cameron Newham and Bill Rosenblatt, published by O'Reilly.