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