29.13. Propagating Shell Functions
One easy way to define shell functions
that you'll have every time you start a new shell is
by defining them in your shell setup
files (Section 3.3). Here are two other
ways.
29.13.1. Exporting bash Functions
In
bash , you can export functions to
other bash subshells (Section 24.4). (The original Korn shell, but not the
public-domain version, supposedly does this too, but I
haven't had much luck with it.) Just use the command
typeset -fx
funcname, where funcname is
the name of the function.
How does this work? It stores the function in an environment variable (Section 35.3) whose value starts with (
). You can see this with printenv or
env (Section 35.3). For example,
let's define a simple function named
dir, export it, start a subshell, run the
function, and look for it in the environment:
bash$ function dir( ) { ls -F "$@"; }
bash$ typeset -fx dir ...export the function
bash$ bash ...start subshell
bash$ dir ...the function still works
,ptbk.last ch14.sgm ch36.ps.gz fmt/
,xrefs.list ch15.ps.gz ch36.sgm gmatlogs/
bash$ printenv
...lots of environment variables...
dir=( ) { ls -F "$@"
}
29.13.2. FPATH Search Path
Both ksh and
zsh will automatically search for functions in the
PATH variable
(Section 35.6). So you can
put a function in a file with the same name as the function (for
instance, put the function foo in a file named
foo), and make the file executable (with
chmod +x foo (Section 35.1)), and then the shell can find the function.
I don't like to use
PATH for function-searching, though. One reason
is that PATH is passed to
all Unix processes -- but if the process
isn't a shell and it tries to execute a function
file, it'll probably fail in an ugly way.[94]
Also, making a file executable if you don't tell the
kernel how to execute it seems to me a recipe for trouble. A better
way to help the shell find functions is to set a function search path
in the FPATH
environment variable; it has the same syntax as
PATH. (In zsh, you can also
set the fpath array -- with the same syntax
as path.) In FPATH, list
directories that hold function files. In ksh,
those files don't even need execute permission! Then
ksh and zsh will search the
FPATH directories if they can't
find an executable file in the PATH.
Would you like the shells to search FPATH before
PATH, so that a function will be executed before
a standard command with the same name? (I would. After all, if I
define a function from a shell prompt or shell setup file like
.zshrc, that function will be run instead of a
standard executable.) Here's how to set that up.
Tell the shell to
autoload the function. Autoloading happens
automatically if there's no
match found in PATH -- because, as I said
above, the shell falls back to FPATH if it
doesn't find a match in PATH.
But if you want the shell to look for a particular name in
FPATH before it tries PATH,
you have to autoload the function. Autoloading a function
doesn't actually define the function (read the
function body into the shell); it simply declares that the function
exists -- so the shell will remember that when you eventually want
to execute the function.
This has a few twists, so let's look at each shell
separately. You might want to do this yourself and follow along: When
I first played with FPATH, I made two
subdirectories of /tmp named
a and b. Each directory had
three simple function files named func1,
func2, and foo. The functions
func1 and func2 simply
echo a message with their name and location.
foo invokes a shell script of the same name, but
first uses set -xv (Section 37.1) for debugging. func1 was a
single-line function and func2 was multiline. The
files in /tmp/a weren't
executable, and the ones in /tmp/b were
executable. I set the FPATH environment variable
(set the shell variable and exported it) to
/tmp/a:/tmp/b -- so the shells should try the
nonexecutable function files before falling back to the executables.
After setting that up, I started a ksh subshell
and played around. Then I exited the
ksh and started a zsh.
29.13.2.1. Korn shell
Here's what happened in
pdksh. The standard
ksh is similar but not as verbose:
$ echo $FPATH
/tmp/a:/tmp/b
$ type func1
func1 is a undefined (autoload from /tmp/a/func1) function
$ func1
This is func1 from /tmp/a, a single-line unexecutable function
$ type func1
func1 is a function
$ typeset -f func2
$ type func2
func2 is a undefined (autoload from /tmp/a/func2) function
$ func2
This is func2 from /tmp/a, a multi-line unexecutable function
$ typeset -f func2
func2( ) {
echo "This is func2 from /tmp/a, a multi-line unexecutable function"
}
$ type foo
foo is /home/jpeek/.bin/foo
$ autoload foo
$ type foo
foo is a undefined (autoload from /tmp/a/foo) function
$ cat /tmp/a/foo
foo( ) { sh -xv $HOME/.bin/foo "$@"; }
$ foo
#!/bin/sh
echo "Welcome to the exciting $0 program..."
+ echo Welcome to the exciting /home/jpeek/.bin/foo program...
Welcome to the exciting /home/jpeek/.bin/foo program...
$ type foo
foo is a function
Here's what happened with func1,
func2, and foo:
-
First, without autoloading, I use type (Section 2.6) to see if
the shell has found func1 anywhere.
There's no func1 along the
PATH, so the shell searches
FPATH -- and finds it. So
func1 is automatically marked for autoloading;
note that I didn't have to autoload it myself
because there's no func1 in a
PATH directory. I run func1,
then use type again; now the shell confirms that
it's read the function definition and
func has been loaded into the shell.
-
Next I played with func2. typeset -f (Section 29.11)
shows that the shell doesn't have a definition for
the function yet, but type shows that the function
declaration has been autoloaded. (This isn't just
academic. If you edit a function definition file,
it's good to know whether the shell has already
loaded a copy of a previous definition.) I run the function, then use
typeset to display the function, which has been
loaded (of course!) by now.
-
Because there's a program named
foo in my PATH,
type shows that. But I want the shell to use my
front-end foo function, so I run
autoload -- and then type
confirms that the shell looked down FPATH and
found the function in /tmp/a. The function
definition hasn't been loaded yet, so I use
cat (Section 12.2)
to display the function file. I run the foo
function; because it set the shell's verbose and
echo flags, you can see the contents of the foo
shell script and the commands that are executed. Finally,
type shows that the shell will now run the
function when I execute foo.
If you'd like to be sure that all the functions in
your FPATH are autoloaded -- especially if
you add new ones pretty often -- here's a way to
do it. Put code like this in your ENV setup
file (Section 3.3):
IFS Section 36.23, for Section 28.9
# Autoload all functions in FPATH directories.
# Temporarily add a colon (:) to IFS to parse FPATH:
old_ifs="$IFS"; IFS=":$IFS"
for d in $FPATH
do autoload `ls $d`
done
IFS="$oldifs"; unset old_ifs
If a directory in FPATH is empty,
autoload
gets no arguments and, in that case, shows the function definitions
it has already autoloaded. I only put a directory in my
FPATH if it has functions to load. If you might
have an empty directory in yours, you can avoid seeing the
autoload output by editing that code to store the
output of ls in a shell variable and running
autoload only if the variable
isn't empty.
29.13.2.2. zsh
The
zsh system
is mostly like ksh. The difference is that
zsh doesn't automatically search
FPATH. You have to manually autoload any
function that you want zsh to search for in
FPATH.
zsh$ echo $FPATH
/tmp/a:/tmp/b
zsh$ type func1
func1 not found
zsh$ func1
zsh: command not found: func1
zsh$ autoload func1
zsh$ type func1
func1 is a shell function
zsh$ func1
This is func1 from /tmp/a, a single-line unexecutable function
zsh$ type func1
func1 is a shell function
zsh$ autoload func2
zsh$ typeset -f func2
undefined func2 ( ) { }
zsh$ func2
This is func2 from /tmp/a, a multi-line unexecutable function
zsh$ typeset -f func2
func2 ( ) {
echo "This is func2 from /tmp/a, a multi-line unexecutable function"
}
zsh$ type foo
foo is /home/jpeek/.bin/foo
zsh$ autoload foo
zsh$ foo
#!/bin/sh
echo "Welcome to the exciting $0 program..."
+ echo Welcome to the exciting /home/jpeek/.bin/foo program...
Welcome to the exciting /home/jpeek/.bin/foo program...
zsh$ type foo
foo is a shell function
I won't repeat all of the explanation from the
ksh section. Instead, let's just
look at the differences:
-
The first examples show that zsh
won't look down FPATH for
func1. Once you autoload the function,
type doesn't give you a clue
whether the function has been defined or just declared.
-
In zsh, you can see whether a function has been
defined by using typeset -f
(instead of type). After autoloading it,
func2 has been declared but not defined. As the
example shows, running the function once loads the definition.
If you'd like to be sure that all the functions in
your FPATH are autoloaded -- especially if
you add new ones pretty often -- here's how to do
it in zsh. Put code like this in a per-shell
setup file (Section 3.3) -- typically .zshrc:
# Autoload all functions in fpath directories:
for d in $fpath
do autoload `ls $d`
done
The code is simpler than in ksh because we can
step through the fpath array without parsing it
at colon (:) characters. As in ksh, though,
you'll want to tweak the code if a directory in
fpath might be empty: store the output of
ls in an array and run autoload
only if the array has members.
-- JP
 |  |  | 29.12. Shell Function Specifics |  | 29.14. Simulated Bourne Shell Functions and Aliases |
Copyright © 2003 O'Reilly & Associates. All rights reserved.
|