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.