Project52.Define Shell Functions


Project 52. Define Shell Functions

"How do I define a shortcut for a commonly used command line, to which I can pass arguments?"

This project explores Bash functions: how to define them; how to handle arguments; and how to list, export, and undefine them. Project 51 covers aliases.

Learn More

Projects 9 and 10 get you started with shell scripting, and the projects in Chapter 9 cover the subject in greater depth.


Functions are similar to aliases. Bash philosophy sees functions replacing aliases in almost all circumstances. The declarative syntax of a function lends itself to a more complex definition than would ever be assigned to an alias. Functions are closer to mini shell scripts but execute in the environment of the current shell. (Scripts are executed by new shell instances.)

Learn More

Project 23 covers the grep command.


Define a Function

Let's illustrate functions by defining one. A good candidate for a simple function is this command line.

$ grep -riw "dear janet" . ./text/jan.txt:Dear Janet


The command performs a recursive, case-insensitive search for whole-word matches, rooted in the current directory. We'll make this into a function and parameterize the search text, thereby encapsulating this useful sequence as a custom command.

To define a function, we must use the correct syntax. Start the definition with the function name followed by (), and enclose the body of the function in braces. Each command in the body, including the last, must be terminated by a semicolon. Be aware that the space after the opening brace must be present.

$ function-name () { command; command; ...;}


A function definition may be split across lines, making entry easier when the body of the function contains many commands. Equivalent to the above example, we could have typed

$ function-name () { > command > command > ... > } $


Tip

The more-please prompt > is held in shell variable PS2 and can be changed by typing

$ PS2="more please> "



You'll notice that when Bash expects more input to complete the command, it'll prompt with > instead of $.

Tip

Make a function permanent by writing it to a Bash configuration file. See Project 47.


Our new function, which we'll call grepx, can be defined by typing

$ grepx () { > grep -riw $* . > }


The sequence $* expands to the text of all arguments passed to the function.

To use our new function, we simply type its name followed by a list of arguments (just as you would for any Unix command or shell script).

$ grepx janet Binary file ./iChats/Janet Forbes on ....ichat matches Binary file ./iChats/Janet Forbes on ....ichat matches ./text/jan.txt:Dear Janet


Debug a Function

There's a problem with this function, which illustrates a common and easy mistake to make. Type the following command.

$ grepx "dear janet" grep: janet: No such file or directory Binary file ./iChats/Janet Forbes on ....ichat matches ./text/jan.txt:Dear Janet


The error grep: janet: No such file or directory was perhaps not something you expected to see. The function as defined has grep searching for the word dear in the files janet and the current directory (.). It's equivalent to the command line

$ grep -riw dear janet .


The problem arises because quoting is lost when $* is expanded. To solve the problem, we must quote $* itself. Redefine the function by typing

$ grepx () { > grep -riw "$*" . > }


Now try again.

$ grepx "dear janet" ./text/jan.txt:Dear Janet


Tip

Export a function. Exporting a function is like exporting a shell variable to make it into an environment variable. (Project 4 explains the difference between shell and environment variables.) Type

$ export -f grepx



Function Arguments

So far, we've expanded "all arguments" by using $*. Argument (or, more strictly, parameter) expansion within a function is very similar to that in a shell script, and Bash functions support the following parameter expansions:

  • $* expands to all arguments.

  • $# expands to the number of argument passed.

  • $n expands to argument number n.

  • $0 expands to the name of the current shell or shell script.

  • $FUNCNAME expands to the function's name.

Enter and run this example function, which illustrates parameter expansion.

$ params () { > echo "$# parameters given" > echo "the second is $2 and the first is $1" > echo "my name is $FUNCNAME and I was called from $0." > } $ params number-one "number two" 2 parameters given the second is number two and the first is number-one my name is params and I was called from -bash.


Let's extend our simple grepx function to take additional arguments, which will be options we pass through to grep. A naïve attempt such as that shown next will not work.

$ grepx -l "dear janet" grep: invalid option -- Usage: grep [OPTION]... PATTERN [FILE]... Try `grep --help' for more information.


Our attempt forms the following command line.

$ grep -ir "-l dear janet" .


We must change grepx to expand its parameters in the form $1 "$2", not "$*". Additionally, the passing of extra options to grep must be made optional, so we have two situations:

  • If $1 starts with a dash (-) character (that is, if it's an option), expand parameters in the form $1 "$2".

  • Otherwise, expand parameters in the form "$1" (or "$*").

Here's our new function, which employs an if-else statement. Remember that the body of a Bash function is just like a shell script.

$ grepx () { > if [ "${1:0:1}" = "-" ] > then >   grep -riw $1 "$2" . > else >   grep -riw "$1" . > fi > }


Learn More

Project 76 covers Bash parameter expansion in more detail.


We'll test the new function by specifying option -l to grep, which says to list the names of matching files but not display the matching line of text.

$ grepx -l "dear janet" ./text/jan.txt


Learn More

Refer to Project 16 for more on command.


We used a neat trick to test the first character of parameter 1 instead of the whole string. The special expansion ${1:0:1} expands $1 but considers only a partial string from character 0 (the first character) of length 1.

Recursive Functions

A function can be recursive that is, it can call itself. I'll not teach recursive programming here but will point out a gotcha that is a side effect of the recursive nature of functions.

We define a function ls to be shorthand for ls -al argument-list. The following attempt doesn't work but gets stuck in a loop.

$ ls () { ls -al $*; }


Function ls attempts to call the Unix command ls, but because recursion is allowed, the function ends up calling itself instead. Futile attempts to call command ls continue foreveror until we interrupt the process by pressing Control-c.

The next version works. We use Bash's built-in command command to make function ls call the Unix command ls instead of itself.

$ ls () { command ls -al $*; }


List All Functions

To list all currently defined functions, type

$ declare -F declare -f grepx declare -f params


Alternatively, specify option -f (lowercase) to list function names and their definitions.

Delete a Function

Delete a function (or two) by using the unset command.

$ unset grepx params


Tcsh Shell Functions

The Tcsh shell does not support functions. However, Tcsh aliases support arguments (see Project 51).




Mac OS X UNIX 101 Byte-Sized Projects
Mac OS X Unix 101 Byte-Sized Projects
ISBN: 0321374118
EAN: 2147483647
Year: 2003
Pages: 153
Authors: Adrian Mayo

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net