August 21, 2013

A bashism a week: function names

The bashism of this week is easy to hit when overriding the execution of a command with a shell function. Think of the following example scenario:

Replacing the yes(1) command with a shell function:
$ exec /bin/bash
$ type -t yes
file
$ yes() { while :; do echo ${1:-y}; done; }
$ type -t yes
function

Now every time yes is called the above-defined shell function will be called instead of /usr/bin/yes.

Apply the same principle to replace the run-parts(8) command with the following overly-simplified shell function:

$ run-parts() { 
    if [ "$1" = "--test" ]; then
        shift;
        simulate=true;
    else
        simulate=false;
    fi;
    for f in "$1"/*; do
        [ -x "$f" ] && [ -f "$f" ] || continue;
        case "$(basename "$f")" in 
            *[!a-zA-Z0-9_/-]*)
                :
            ;;
            *)
                if $simulate; then
                    echo $f;
                else
                    $f;
                fi
            ;;
        esac;
    done
}
$ type -t run-parts
function
(note the use of negative matching)

It also works as expected. However, when running it under a shell that only supports the function names required by the POSIX:2001 specification it will fail. One such shell is dash, which aborts with a "Syntax error: Bad function name", another is posh which aborts with a "run-parts: invalid function name".

If you ever want to have function names with dashes, equal signs, commas, and other unusual characters make sure you use bash and ksh-like shells (and keep that code to yourself). Yes, you can even have batch-like silencing of stdout with
function @ { "$@" > /dev/null ; }

Update: there were missing quotation marks in the example @ function.

6 comments:

  1. You probably should quote the arguments:

    function @ { "$@" > /dev/null ; }

    ReplyDelete
  2. Defining a function _run_parts followed by:

    alias run-parts=_run_parts

    should work with a non-bash shell ...

    ReplyDelete
    Replies
    1. Apologies for responding until now.

      There are three problems with that:
      1. Aliases are not POSIX :-)
      2. Aliases are not functions (yeah, being picky here for the fun)
      3. Aliases are not expanded in bash in non-interactive mode. You need to setopt first, in which case you could as well just use a non-standard function name.

      Delete
    2. Sorry, just found out about your reply today ...

      According to http://pubs.opengroup.org/onlinepubs/007904975/utilities/xcu_chap02.html#tag_02_03_01 aliases actually seem to be in the POSIX standard.

      Delete
    3. In my blogpost about shell aliases I gave a bit more details: they are an extension (note the XSI marker).

      Delete