FUNCTIONS & SIGNALS
6.1. Signals
Finding the signal man page
Your system contains a man page listing all the available signals, but depending on your operating system, it might be opened in a different way. On most Linux systems, this will be man 7 signal. When in doubt, locate the exact man page and section using commands like
man -k signal | grep list
or
apropos signal | grep list
Signal names can be found using kill -l.
Signals to your Bash shell
In the absence of any traps, an interactive Bash shell ignores SIGTERM and SIGQUIT. SIGINT is caught and handled, and if job control is active, SIGTTIN, SIGTTOU and SIGTSTP are also ignored. Commands that are run as the result of a command substitution also ignore these signals, when keyboard generated.
SIGHUP by default exits a shell. An interactive shell will send a SIGHUP to all jobs, running or stopped; see the documentation on the disown built-in if you want to disable this default behavior for a particular process. Use the huponexit option for killing all jobs upon receiving a SIGHUP signal, using the shopt built-in.
Sending signals using the shell
Control signals in Bash
Standard key combination | Meaning |
Ctrl+C | The interrupt signal, sends SIGINT to the job running in the foreground. |
Ctrl+Y | The delayed suspend character. Causes a running process to be stopped when it attempts to read input from the terminal. Control is returned to the shell, the user can foreground, background or kill the process. Delayed suspend is only available on operating systems supporting this feature. |
Ctrl+Z | The suspend signal, sends a SIGTSTP to a running program, thus stopping it and returning control to the shell. |
Usage of signals with kill
Most modern shells, Bash included, have a built-in kill function. In Bash, both signal names and numbers are accepted as options, and arguments may be job or process IDs. An exit status can be reported using the -l option: zero when at least one signal was successfully sent, non-zero if an error occurred.
Using the kill command from /usr/bin, your system might enable extra options, such as the ability to kill processes from other than your own user ID and specifying processes by name, like with pgrep and pkill.
Both kill commands send the TERM signal if none is given.
This is a list of the most common signals:
Signal name | Signal value | Effect |
SIGHUP | 1 | Hang-up |
SIGINT | 2 | Interrupt from keyboard |
SIGKILL | 9 | Kill signal |
SIGTERM | 15 | Termination signal |
SIGSTOP | 17,19,23 | Stop the process |
SIGKILL and SIGSTOP cannot be caught, blocked or ignored.
When killing a process or series of processes, it is common sense to start trying with the least dangerous signal, SIGTERM. That way, programs that care about an orderly shutdown get the chance to follow the procedures that they have been designed to execute when getting the SIGTERM signal, such as cleaning up and closing open files. If you send a SIGKILL to a process, you remove any chance for the process to do a tidy cleanup and shutdown, which might have unfortunate consequences.
But if a clean termination does not work, the INT or KILL signals might be the only way. For instance, when a process does not die using Ctrl+C, it is best to use the kill -9 on that process ID.
6.2. Traps
General
There might be situations when you don’t want users of your scripts to exit untimely using keyboard abort sequences, for example because input has to be provided or cleanup has to be done. The trap statement catches these sequences and can be programmed to execute a list of commands upon catching those signals.
The syntax for the trap statement is straightforward:
trap [COMMANDS] [SIGNALS]
This instructs the trap command to catch the listed SIGNALS, which may be signal names with or without the SIG prefix, or signal numbers. If a signal is 0 or EXIT, the COMMANDS are executed when the shell exits. If one of the signals is DEBUG, the list of COMMANDS is executed after every simple command. A signal may also be specified as ERR; in that case COMMANDS are executed each time a simple command exits with a non-zero status. Note that these commands will not be executed when the non-zero exit status comes from part of an if statement, or from a while or until loop. Neither will they be executed if a logical AND (&&) or OR (||) result in a non-zero exit code, or when a command’s return status is inverted using the ! operator.
The return status of the trap command itself is zero unless an invalid signal specification is encountered. The trap command takes a couple of options, which are documented in the Bash info pages.
How Bash interprets traps
When Bash receives a signal for which a trap has been set while waiting for a command to complete, the trap will not be executed until the command completes. When Bash is waiting for an asynchronous command via the wait built-in, the reception of a signal for which a trap has been set will cause the wait built-in to return immediately with an exit status greater than 128, immediately after which the trap is executed.
Activity:
o Detecting when a variable is used.
When debugging longer scripts, you might want to give a variable the trace attribute and trap DEBUG messages for that variable. Normally you would just declare a variable using an assignment like VARIABLE=value. Replacing the declaration of the variable with the following lines might provide valuable information about what your script is doing:
declare -t VARIABLE=value
trap “echo VARIABLE is being used here.” DEBUG
# rest of the script
o Removing rubbish upon exit.
The whatis command relies on a database which is regularly built using the makewhatis.cron script with cron:
#!/bin/bash
LOCKFILE=/var/lock/makewhatis.lock
# Previous makewhatis should execute successfully:
[ -f $LOCKFILE ] && exit 0
# Upon exit, remove lockfile.
trap “{ rm -f $LOCKFILE ; exit 255; }” EXIT
touch $LOCKFILE
makewhatis -u -w
exit 0
o Program not to allow user to user Ctrl+C in the shell execution.
#!/bin/bash
trap “echo You are not allowed to use Ctrl+C” SIGINT
echo -n “Please give your name: “
read name
echo “Thank you – You are $name”
6.3. Functions
What are functions?
Shell functions are a way to group commands for later execution, using a single name for this group, or routine. The name of the routine must be unique within the shell or script. All the commands that make up a function are executed like regular commands. When calling on a function as a simple command name, the list of commands associated with that function name is executed. A function is executed within the shell in which it has been declared: no new process is created to interpret the commands.
Special built-in commands are found before shell functions during command lookup. The special built-ins are: break, :, ., continue, eval, exec, exit, export, readonly, return, set, shift, trap and unset.
Function syntax
Functions either use the syntax
function FUNCTION { COMMANDS; }
or
FUNCTION () { COMMANDS; }
Both define a shell function FUNCTION. The use of the built-in command function is optional; however, if it is not used, parentheses are needed.
The commands listed between curly braces make up the body of the function. These commands are executed whenever FUNCTION is specified as the name of a command. The exit status is the exit status of the last command executed in the body.
Positional parameters in functions
Functions are like mini-scripts: they can accept parameters, they can use variables only known within the function (using the local shell built-in) and they can return values to the calling shell.
A function also has a system for interpreting positional parameters. However, the positional parameters passed to a function are not the same as the ones passed to a command or script.
When a function is executed, the arguments to the function become the positional parameters during its execution. The special parameter # that expands to the number of positional parameters is updated to reflect the change. Positional parameter 0 is unchanged. The Bash variable FUNCNAME is set to the name of the function, while it is executing.
If the return built-in is executed in a function, the function completes and execution resumes with the next command after the function call. When a function completes, the values of the positional parameters and the special parameter # are restored to the values they had prior to the function’s execution. If a numeric argument is given to return, that status is returned.
Displaying functions
All functions known by the current shell can be displayed using the set built-in without options. Functions are retained after they are used, unless they are unset after use. The which command also displays functions.
Recycling
There are plenty of scripts on your system that use functions as a structured way of handling series of commands. On some Linux systems, for instance, you will find the /etc/rc.d/init.d/functions definition file, which is sourced in all init scripts. Using this method, common tasks such as checking if a process runs, starting or stopping a daemon and so on, only have to be written once, in a general way. If the same task is needed again, the code is recycled.
You could make your own /etc/functions file that contains all functions that you use regularly on your system, in different scripts. Just put the line
. /etc/functions
somewhere at the start of the script and you can recycle functions.
Activity:
o Create a normal function in a script to execute set of commands.
#!/bin/bash
function listdb
{
if [ -f /etc/oratab ]
then
cat /etc/oratab | grep -v ^# | awk ‘FS=”:” {print $1}’|
grep -v ^$
else
echo “Oracle database not installed on this server”
fi
}
listdb
o Create a function on command line.
function listdb
{
if [ -f /etc/oratab ]
then
cat /etc/oratab | grep -v ^# | awk ‘FS=”:” {print $1}’| grep -v ^$
else
echo “Oracle database not installed on this server”
fi
}
listdb
6.4. Common shell features
The following features are standard in every shell. Note that the stop, suspend, jobs, bg and fg commands are only available on systems that support job control.
Common shell features list.
Command | Meaning |
> | Redirect output |
>> | Append to file |
< | Redirect input |
<< | “Here” document (redirect input) |
| | Pipe output |
& | Run process in background. |
; | Separate commands on same line |
* | Match any character(s) in filename |
? | Match single character in filename |
[] | Match any characters enclosed |
() | Execute in subshell |
“ | Substitute output of enclosed command |
” “ | Partial quote (allows variable and command expansion) |
‘ ‘ | Full quote (no expansion) |
\ | Quote following character |
$var | Use value for variable |
$$ | Process id |
$0 | Command name |
$n | nth argument (n from 0 to 9) |
# | Begin comment |
bg | Background execution |
break | Break from loop statements |
cd | Change directories |
continue | Resume a program loop |
echo | Display output |
eval | Evaluate arguments |
exec | Execute a new shell |
fg | Foreground execution |
jobs | Show active jobs |
kill | Terminate running jobs |
newgrp | Change to a new group |
shift | Shift positional parameters |
stop | Suspend a background job |
suspend | Suspend a foreground job |
time | Time a command |
umask | Set or list file permissions |
unset | Erase variable or function definitions |
wait | Wait for a background job to finish |
6.5. Differing shell features
The table below shows major differences between the standard shell (sh), Bourne Again SHell (bash), Korn shell (ksh) and the C shell (csh).
sh | bash | ksh | csh | Meaning/Action |
$ | $ | $ | % | Default user prompt |
>| | >| | >! | Force Redirection | |
> file 2>&1 | &> file or > file 2>&1 | > file 2>&1 | >& file | Redirect stdout and stderr to file |
{} | {} | Expand elements in list | ||
`command` | `command` or $(command) | $(command) | `command` | Substitute output of enclosed command |
$HOME | $HOME | $HOME | $home | Home directory |
~ | ~ | ~ | Home directory symbol | |
~+, ~-, dirs | ~+, ~- | =-, =N | Access directory stack | |
var=value | VAR=value | var=value | set var=value | Variable assignment |
export var | export VAR=value | export var=val | setenv var val | Set environment variable |
${nnnn} | ${nn} | More than 9 arguments can be referenced | ||
“$@” | “$@” | “$@” | All arguments as separate words | |
$# | $# | $# | $#argv | Number of arguments |
$? | $? | $? | $status | Exit status of the most recently executed command |
$! | $! | $! | PID of most recently backgrounded process | |
$- | $- | $- | Current options | |
. file | source file or . file | . file | source file | Read commands in file |
alias x=’y’ | alias x=y | alias x y | Name x stands for command y | |
case | case | case | switch or case | Choose alternatives |
done | done | done | end | End a loop statement |
esac | esac | esac | endsw | End case or switch |
exit n | exit n | exit n | exit (expr) | Exit with a status |
for/do | for/do | for/do | foreach | Loop through variables |
set -f, set -o nullglob|dotglob|nocaseglob|noglob | noglob | Ignore substitution characters for filename generation | ||
hash | hash | alias -t | hashstat | Display hashed commands (tracked aliases) |
hash cmds | hash cmds | alias -t cmds | rehash | Remember command locations |
hash -r | hash -r | unhash | Forget command locations | |
history | history | history | List previous commands | |
ArrowUp+Enter or !! | r | !! | Redo previous command | |
!str | r str | !str | Redo last command that starts with “str” | |
!cmd:s/x/y/ | r x=y cmd | !cmd:s/x/y/ | Replace “x” with “y” in most recent command starting with “cmd”, then execute | |
if [ $i -eq 5 ] | if [ $i -eq 5 ] | if ((i==5)) | if ((i==5)) | Sample condition test |
fi | fi | fi | endif | End if statement |
ulimit | ulimit | ulimit | limit | Set resource limits |
pwd | pwd | pwd | dirs | Print working directory |
read | read | read | $< | Read from terminal |
trap 2 | trap 2 | trap 2 | onintr | Ignore interrupts |
unalias | unalias | unalias | Remove aliases | |
until | until | until | Begin until loop | |
while/do | while/do | while/do | while | Begin while loop |
To start reading from Chapter 1. please click here