A ''wrapper script'' is one of the most common uses for shell script. Rather than attempting to reimplement the functionality of an existing tool, a wrapper's job is to pass along its inputs to some other tool, with slight modifications. This saves a tremendous amount of labor. The most basic form of a wrapper script is this: {{{#!highlight bash #!/bin/sh exec /some/weird/place/toolname ${1+"$@"} }}} Generally, when writing a wrapper, we use the most portable syntax possible. There's normally no need for fancy, shell-specific or platform-specific code if all we're doing is passing along inputs to some other program. In fact, the code above could probably have been replaced with a symbolic link. A slightly more realistic wrapper script might look something like this: {{{#!highlight bash #!/bin/sh CDR_SECURITY=8:dvd,clone,.... export CDR_SECURITY exec cdrecord-prodvd ${1+"$@"} }}} This wrapper sets an environment variable, and then invokes another program with the same arguments and input that the wrapper script received. The `#!/bin/sh` shebang is used because we don't need anything that's not present in the vanilla Bourne shell. We're sticking to the most portable syntax, so this wrapper could run on any Unix system we're likely to find. Thus, the `export` is on a separate line, ''after'' the variable it's exporting has been set to its value. The `exec` causes the tool we're wrapping to have the same PID that we have. This is important if the process is being [[ProcessManagement|managed]] by something that wants to maintain a parent/child relationship with it for purposes of sending signals, or simply for recording the process's PID. Also, there's no need to leave an instance of the shell lying around in memory; the `exec` saves one process fork, and is therefore quite efficient. The `${1+"$@"}` hack is used instead of `"$@"` because some old versions of the Bourne shell had a bug, in which an empty argument list would cause `"$@"` to expand to a single string of length 0 (that is, `''`) instead of an empty argument list. The `${1+...}` syntax gets around that by checking first whether we ''have'' a non-empty argument list. If we do, then we use `"$@"` to pass it along; otherwise, we don't pass anything. You'll see this construct many, many times. (For more examples of fancy parameter expansions, see [[BashFAQ/073|Bash FAQ #73]].) Recall that `"${1+"$@"}"`, `"$1"`, `"${a[0]}"`, and so forth, will all expand to at least one word when double quoted, even if unset. That's why the double quotes are ''omitted'' around `${1+"$@"}`. Another, more complicated, case of a wrapper script is a [[http://cr.yp.to/daemontools.html|daemontools]] ''run script''. Services that are managed by daemontools rely on a program (written by the system administrator, usually in Bourne shell) to set up the environment and execute the process that's being managed. Here's a (somewhat shortened) example: {{{#!highlight bash #!/bin/sh exec /usr/local/bin/softlimit -m 3000000 \ /usr/local/bin/tcpserver -v -x /etc/tcp.smtp.cdb -u 10092 -g 10098 0 smtp \ /usr/local/bin/rblsmtpd -r zen.spamhaus.org \ /var/qmail/bin/qmail-smtpd 2>&1 }}} This example is interesting because it's really an onion-like set of layers. (All of [[http://cr.yp.to/djb.html|DJB]]'s software is designed that way.) Our run script is a Bourne shell script that `exec`s a program called `softlimit`. `softlimit`'s job is simply to set up process resource limits (see `setrlimit(2)` on GNU/Linux). We could use a shell's `ulimit` or `limit` command to do the same thing, but remember that we're writing in Bourne shell, which ''doesn't have that''! So, DJB wrote `softlimit` as a wrapper program (in C) which sets the limits and then `exec`s the next process in the chain. (If we were writing our run script in bash instead of Bourne shell, then calling `ulimit` would be efficient and desirable.) The `2>&1` at the end of the `exec softlimit` command binds standard error and standard output together, because another process (not shown) is logging all of this stuff. The next process in the chain is `tcpserver` from DJB's [[http://cr.yp.to/ucspi-tcp.html|ucspi-tcp]] suite. This inherits the limits already set by the previous elements of the chain. Then, it reads the `/etc/tcp.smtp.cdb` file, and starts listening on the `smtp` port of network interface `0` (all interfaces). When it receives a connection, it forks and executes the `rblsmtpd` program as UID 10092 and GID 10098. (It also sets several different environment variables, but that's a bit beyond the scope of this page.) The `rblsmtpd` program inspects its environment variables (inherited from earlier in the process chain), and based on those, it'll either do DNS lookups of the connection's source IP address, or skip those lookups. If the lookups are performed and find a "spam sender" result, an error is written, and `rblsmtpd` exits. Otherwise, `rblsmtpd` `exec`s the next process in the chain. Finally, if we got this far, `qmail-smtpd` actually receives an email from the sender and puts it in the queue for processing. ---- CategoryShell CategoryUnix