Bourne Shell Introspection
So I was thinking about how to refactor our custom Linux and Solaris init scripts at work. The way FreeBSD does it is to have the scripts in /etc/rc.d define variables with the commands to execute, e.g.,
start_cmd='/usr/sbin/foobard' stop_cmd='kill `cat /var/run/foobar.pid`' run_rc_command "$1"
where $1 is “start”, “stop”, or whatever, and run_rc_command is a function loaded from an external file. It can check whether $stop_cmd is defined, and if not, take some default action.
This is great and all, but I was wondering whether it would be possible to check whether a given shell function exists. That way, a common file could implement a generic structure for starting and stopping daemons, and the daemon-specific file could just set the specifics by defining do_start and do_stop functions.
The way to do this in Perl is to iterate over the symbol table of the package you’re looking for, and seeing whether each entry is a function. The symbol table for Foo::Bar is %Foo::Bar::; for the main package, it’s %::. Thus:
while (my ($k, $v) = each %::)
{
if (defined())
{
print "$k is a functionn";
}
}
sub test_x() {}
sub test_y() {}
sub test_z() {}
But I didn’t know how to do it in the Bourne shell.
Enter type, which tells you exactly that:
#!/bin/sh
# List of all known commands
STD_CMDS="start stop restart status verify"
MORE_CMDS="graceful something_incredibly_daemon_specific"
do_start="This is a string, not a function"
do_restart() {
echo "I ought to restart something"
}
do_graceful() {
echo "I am so fucking graceful"
}
for cmd in ${STD_CMDS} ${MORE_CMDS}; do
if type "do_$cmd" >/dev/null 2>&1; then
echo "* do_$cmd is defined"
else
echo "- do_$cmd is not defined"
fi
done
And yes, this works not just in bash, but in the traditional, bourne-just-once shell, on every platform that I care about.
So yay, it turns out that the Bourne shell has more introspection than
I thought.