In the previous tutorial, Bash Shell and LXterminal to launch Linux Console for the bash shell in Raspbian, was demonstrated. Now, it’s time to start shell scripting using bash shell.
The shell scripts are made up of shell commands. The shell commands in the bash shell are called bash commands after the name of the respective shell. The shell commands and their syntax remain more or less the same irrespective of the shell. So, bash commands and their syntax is almost similar to shell commands for any other Linux shell like Csh, Ksh, and Tcsh etc.
Fig. 1: Screenshot of Linux Terminal Console on Raspbian
Bash Shell –
Bash stands for ‘Bourne Again Shell’. Bourne shell has been the traditional UNIX shell. Bash has been developed similar to Bourne shell inheriting all built-in commands of the Bourne shell. Bash uses rules for evaluation and quoting of shell commands from ‘POSIX’ specification for the standard UNIX shell.
The shell reads input from a file (shell script), string supplied as argument to –c invocation option or user’s terminal (Linux console). It then, breaks the input into words and operators that are separated by meta-characters according to the quoting rules. At the same time, it expands any alias used in the input. The separated tokens are parsed as simple and compound commands. The tokens are expanded into lists of filenames, commands and arguments. If arguments contain any redirections, they are performed and removed from the arguments thereafter. Finally, the shell executes the commands one after the other (the interpreter way) and waits for commands to complete collecting exit status for each command.
On user terminal, the commands can be executed only one by one. The commands can be grouped in a text file called shell script (with .sh extension) when a sequence of commands has to be executed one after the other.
Shell Syntax –
The input to the shell is divided into words and operators which are separated by meta-characters. The tokens in an input are parsed according to certain quoting rules. The bash obeys the following quoting rules –
1) The non-quoted backslash () is bash escape character. It preserves the literal value of the character next to it with an exception for the newline character. The newline with non-quoted backslash is treated as line continuation.
2) The single quotes (‘’) preserve the literal value of each character enclosed within the quotes. The single quotes within single quotes are not allowed even with backslash character.
3) The double quotes preserve the literal value of all the character enclosed within the quotes with the exception of ‘$’, ‘’’, ‘’ characters and ‘!’ character when history expansion is enabled. The characters $ and ‘retains special meaning within double quotes. The backslash retains special meaning when is followed by $, ‘, “or characters. The backslash preceding these characters are removed while backslash preceding any other characters are left unmodified. The double quotes within the double quotes can be used if preceded by backslash.
4) The words having the form $’string’ have special meanings and are replaced as per specified in ANSI C standard.
5) The backslash preceding specific characters are escape sequences and have special meaning. There are the following escape sequences that are cited as backslash preceding specific characters –
Fig. 2: Table Listing Escape Sequences in Linux Console
6) If double quotes are followed by$ character, the string enclosed within double quotes is translated to current locale. If current locale is C or POSIX, $ character after double quotes is ignored.
7) The comments start with # character. All characters following # on a line are treated as comment and are ignored by the shell.
Simple, Complex and Compound Shell Commands –
The shell commands can be simple commands or compound commands. A simple command is a group of words separated by blanks and group terminated by a shell’s control operator. The first word specifies the command to be executed while the rest of the words are arguments for the command. On execution of a simple command, shell gives a return status citing the exit status of the command, like if command has been successfully executed or not, and if yes then its results.
The complex commands are composed of several simple commands arranged together in a variety of ways like in a pipeline where output of one command is input of other, in a loop construct or in a conditional construct.
For example, pipeline is a sequence of one or more commands separated by one of the control operators ‘|’ or ‘|&’. The output of each command in the pipeline is connected via a pipe to the input of the next command. That is, each command reads the previous command’s output. This connection is performed before any redirections specified by the command. If ‘|&’ is used, command1’s standard error, in addition to its standard output, is connected to command2’s standard input through the pipe. This implicit redirection of the standard error to the standard output is performed after any redirections specified by the command. Each command in a pipeline is executed in its own subshell. The exit status of a pipeline is the exit status of the last command in the pipeline, unless the pipefail option is enabled. If pipefail is enabled, the pipeline’s return status is the value of the last (rightmost) command to exit with a non-zero status, or zero if all commands exit successfully. If the reserved word ‘!’ precedes the pipeline, the exit status is the logical negation of the exit status. The shell waits for all commands in the pipeline to terminate before returning a value.
The list of commands is another form of complex commands. A list is a sequence of one or more pipelines separated by one of the operators ‘;’, ‘&’, ‘&&’, or ‘||’, and optionally terminated by one of ‘;’, ‘&’, or a newline. Of these list operators, ‘&&’ and ‘||’ have equal precedence, followed by ‘;’ and ‘&’, which have equal precedence. A sequence of one or more newlines may appear in a list to delimit commands, equivalent to a semicolon. If a command is terminated by the control operator ‘&’, the shell executes the command asynchronously in a subshell. This is known as executing the command in the background. The shell does not wait for the command to finish, and the return status is 0 (true). Commands separated by a ‘;’ are executed sequentially; the shell waits for each command to terminate in turn. The return status is the exit status of the last command executed. The ‘and’ and ‘or’ lists are sequences of one or more pipelines separated by the control operators ‘&&’ and ‘||’, respectively. ‘And’ and ‘or’ lists are executed with left associativity. When separated by and (&&) operator, right hand side command is executed if and only if left hand side command returns an exit status of zero. When separated by or (||) operator, right hand side command is executed if and only if left hand side command returns a non-zero exit status. The return status of and and or lists is the exit status of the last command executed in the list.
The compound commands are shell programming constructs where each construct begins with a reserved word or control operator and is terminated by a corresponding reserved word or operator. Any redirections associated with a compound command apply to all commands within that compound command unless explicitly overridden. In most cases a list of commands in a compound command’s description may be separated from the rest of the command by one or more newlines, and may be followed by a newline in place of a semicolon. The looping constructs available to form compound commands include until, while and for. The conditional constructs available to form compound commands include if-then-elif—then-fi, case, select, (()) and [].In case of ((expression)), if the value of the expression is non-zero, the return status is 0; otherwise the return status is 1. The [[expression]] return a status of 0 or 1 depending on the evaluation of the conditional expression.
The commands can be grouped to execute as a unit by either enclosing list of commands between parentheses or by placing the list of commands between curly braces. On placing a list of commands between parentheses causes a subshell environment to be created and each of the commands in list to be executed in that subshell. Since the list is executed in a subshell, variable assignments do not remain in effect after the subshell completes. On placing a list of commands between curly braces causes the list to be executed in the current shell context. No subshell is created. The semicolon (or newline) following list is required.
The braces are reserved words, so they must be separated from the list by blanks or other shell meta characters. The parentheses are
operators, and are recognized as separate tokens by the shell even if they are not separated from the list by whitespace. The exit status of both of these constructs is the exit status of the list.
The shell commands can also be grouped as functions. The commands grouped in a function executes whenever the function is called. The functions are executed within current shell context without creating any new process.
All the words in a shell command belong to a type. There are the following five types of words in any shell command – Alias, Function, Shell Built-in, Keyword and File.
Shell Scripts –
Shell scripts are text files containing shell commands. When these files are used as the first non-option argument when invoking Bash, and neither the -c nor -s option is supplied Bash reads and executes commands from the file, then exits. Script files have a .sh extension. Their execution creates a non-interactive shell. The shell first searches for the file in the current directory, and looks in the directories in $PATH if not found there. When Bash runs a shell script, it sets the special parameter 0 to the name of the file, rather than the name of the shell, and the positional parameters are set to the remaining arguments, if any are given. If no additional arguments are supplied, the positional parameters are unset.
A shell script may be made executable by using the chmod command to turn on the execute bit. When Bash finds such a file while searching the $PATH for a command, it spawns a subshell to execute it. A shell script can be executed in the terminal by passing the following commands –
Or passing the following command –
Bash Filename Arguments
If the first line of a script begins with the two characters ‘#!’ the remainder of the line specifies an interpreter for the program. Apart from Bash any other interpreter like awk, Pearl etc can be specified in the shell script. This line specifying interpreter for the shell script is called shebang.
Let’s write the first script –
Open LXterminal by navigating to Accessories -> Terminal. In the terminal open nano editor and create a file named hello-world.sh by passing the following commands –
Sudo nano hello-world.sh
This will open a text editor within the terminal window. Press I to enter insert mode and write the following shell commands –
Echo “hello world”
Exit and save the file in Nano by pressing Ctrl-X to save and exit.
Let us see what each line in the script is doing –
#! /bin/bash: This is the first line of any shell script. It is called shebang. It specifies that the script must be interpreted by the bash shell. If this line is not included in the script, then the commands will run within the current shell which can create problem when another shell is initiated.
Echo “hello world”: Echo is a shell built-in command that is used to write standard output by default to the screen. Here double quotes are used to contain the text string that must be printed by the echo command.
Exit 0: Exit is a shell built-in command that is used to exit the script. The command is supplied with an integer argument. The integer argument 0 specifies the exit status script being successfully being executed. Any other value than 0 indicate some kind of error in the script execution.
Executing the script –
On saving the script in the nano editor, it is saved to the HOME folder. Here a bin folder is created to save our scripts. Though being saved to PATH environment, it will not execute as a standalone script. The script file must be assigned permissions for making it executable. The script can be checked if it is executable by passing the following command in the terminal –
$ bash $HOME/bin/hello-world.sh
The script file can be assigned executable permissions by passing the following command in the terminal –
$ chmod +x $HOME/bin/hello-world.sh
Or by passing the following command in the terminal –
sudo chmod +x $HOME/bin/hello-world.sh
chmod is a shell built-in function that is used to change permissions of files and directories. The letter –x is an argument that specifies execute. The other argument is the PATH variable containing the script file.
Running the Script –
After being made executable, the script file can be run by passing the following command in the terminal –
On running this script, the text string “hello world” will be printed on the terminal window. This was a simple script with no real use. However, now it is learnt how to write scripts (using nano editor), make them executable and run them from the terminal. It must be noted that user must navigate to the directory containing the script file ($HOME/bin in this case) before running the script.
In the next tutorial, shell built-in commands for handling files and directories will be discussed.
Filed Under: Featured Contributions