Functions and aliases appear similar at a casual glance, but behave slightly differently. The big difference is that function arguments can be used anywhere within the body of the function, while an alias simply appends arguments to the end of the command.
Functions and arguments
How to do it...
A function is defined with the function command, a function name, open/close parentheses, and a function body enclosed in curly brackets:
- A function is defined as follows:
function fname() { statements; }
Alternatively, it can be defined as:
fname() { statements; }
It can even be defined as follows (for simple functions):
fname() { statement; }
- A function is invoked using its name:
$ fname ; # executes function
- Arguments passed to functions are accessed positionally, $1 is the first argument, $2 is the second, and so on:
fname arg1 arg2 ; # passing args
The following is the definition of the function fname. In the fname function, we have included various ways of accessing the function arguments.
fname() { echo $1, $2; #Accessing arg1 and arg2 echo "$@"; # Printing all arguments as list at once echo "$*"; # Similar to $@, but arguments taken as single entity return 0; # Return value }
Arguments passed to scripts can be accessed as $0 (the name of the script):
-
- $1 is the first argument
- $2 is the second argument
- $n is the nth argument
- "$@" expands as "$1" "$2" "$3" and so on
- "$*" expands as "$1c$2c$3", where c is the first character of IFS
- "$@" is used more often than $*, since the former provides all arguments as a single string
- Compare alias to function
- Here's an alias to display a subset of files by piping ls output to grep. The argument is attached to the end of the command, so lsg txt is expanded to ls | grep txt:
$> alias lsg='ls | grep' $> lsg txt file1.txt file2.txt file3.txt
- If we wanted to expand that to get the IP address for a device in /sbin/ifconfig, we might try the following:
$> alias wontWork='/sbin/ifconfig | grep' $> wontWork eth0 eth0 Link encap:Ethernet HWaddr 00:11::22::33::44:55
- The grep command found the eth0 string, not the IP address. If we use a function instead of an alias, we can pass the argument to the ifconfig, instead of appending it to the grep:
$> function getIP() { /sbin/ifconfig $1 | grep 'inet '; } $> getIP eth0 inet addr:192.168.1.2 Bcast:192.168.255.255 Mask:255.255.0.0
There's more...
Let's explore more tips on Bash functions.
The recursive function
Functions in Bash also support recursion (the function can call itself). For example, F() { echo $1; F hello; sleep 1; }.
Fork bomb
A recursive function is a function that calls itself: recursive functions must have an exit condition, or they will spawn until the system exhausts a resource and crashes.
This function: :(){ :|:& };: spawns processes forever and ends up in a denial-of-service attack.
The & character is postfixed with the function call to bring the subprocess into the background. This dangerous code forks processes forever and is called a fork bomb.
You may find it difficult to interpret the preceding code. Refer to the Wikipedia page h t t p ://e n . w i k i p e d i a . o r g /w i k i /F o r k _ b o m b for more details and interpretation of the fork bomb.
Prevent this attack by restricting the maximum number of processes that can be spawned by defining the nproc value in /etc/security/limits.conf.
This line will limit all users to 100 processes:
hard nproc 100
Exporting functions
Functions can be exported, just like environment variables, using the export command. Exporting extends the scope of the function to subprocesses:
export -f fname $> function getIP() { /sbin/ifconfig $1 | grep 'inet '; } $> echo "getIP eth0" >test.sh $> sh test.sh sh: getIP: No such file or directory $> export -f getIP $> sh test.sh inet addr: 192.168.1.2 Bcast: 192.168.255.255 Mask:255.255.0.0
Reading the return value (status) of a command
The return value of a command is stored in the $? variable.
cmd; echo $?;
The return value is called exit status. This value can be used to determine whether a command completed successfully or unsuccessfully. If the command exits successfully, the exit status will be zero, otherwise it will be a nonzero value.
The following script reports the success/failure status of a command:
#!/bin/bash #Filename: success_test.sh # Evaluate the arguments on the command line - ie success_test.sh 'ls | grep txt' eval $@ if [ $? -eq 0 ]; then echo "$CMD executed successfully" else echo "$CMD terminated unsuccessfully" fi
Passing arguments to commands
Most applications accept arguments in different formats. Suppose -p and -v are the options available, and -k N is another option that takes a number. Also, the command requires a filename as argument. This application can be executed in multiple ways:
- $ command -p -v -k 1 file
- $ command -pv -k 1 file
- $ command -vpk 1 file
- $ command file -pvk 1
Within a script, the command-line arguments can be accessed by their position in the command line. The first argument will be $1, the second $2, and so on.
This script will display the first three command line arguments:
echo $1 $2 $3
It's more common to iterate through the command arguments one at a time. The shift command shifts eachh argument one space to the left, to let a script access each argument as $1. The following code displays all the command-line values:
$ cat showArgs.sh for i in `seq 1 $#` do echo $i is $1 shift done $ sh showArgs.sh a b c 1 is a 2 is b 3 is c