POSIX shell compatible and POSIX conforming dirname implementation The discussion in the comments of this answer made me wonder how hard it would be to write a built-in only version of dirname.

This is what I ended up with but I'm reasonably confident it should be able to be improved (I haven't spent much time thinking about it yet).

Any improvements/suggestions are welcome.

#!/bin/sh

p=$1
alt=$2
skip78=

stripslashes() {
    i=$1
    while [ "$i" != "$o" ]; do
        o=$i
        i=${i%/}
    done
    eval "$2=\$i"
}

[ "$p" = '//' ] || {
    case "$p" in
        *[!/]*|'')
            stripslashes "$p" p
            case "$p" in
                */*)
                    p=${p%/*}
                    ;;
                *)
                    p=.
                    skip78=skip78
                    ;;
            esac
            ;;
        *)
            p=/
            skip78=skip78
            ;;
    esac
}
[ -n "$skip78" ] || {
    { [ "$p" != '//' ] || [ -z "$alt" ]; } &amp;&amp; {
        stripslashes "$p" p
        [ -z "$p" ] &amp;&amp; p=/
    }
}

printf -- %s\\n "$p"


The POSIX spec for dirname is:


  
  If string is //, skip steps 2 to 5.
  If string consists entirely of &lt;slash> characters, string shall be set to a single &lt;slash> character. In this case, skip
  steps 3 to 8.
  If there are any trailing &lt;slash> characters in string, they shall be removed.
  If there are no &lt;slash> characters remaining in string, string shall be set to a single &lt;period> character. In this case, skip steps 5 to 8.
  If there are any trailing non- &lt;slash> characters in string, they shall be removed.
  If the remaining string is //, it is implementation-defined whether steps 7 and 8 are skipped or processed.
  If there are any trailing &lt;slash> characters in string, they shall be removed.
  If the remaining string is empty, string shall be set to a single &lt;slash> character.
  


Update: Made a gist.
I don't think there's a really compelling reason for this eval in stripslashes:


eval "$2=\$i"



It's a dirty hack to update the parameter variable in-place.
But this is not a common practice, I don't know other commands or built-ins that work that way, and I would find such usage unexpected.
I would find it more usable if stripslashes behaved more like other commands do:


print the result on stdout
set the result in a designated variable (like REPLY)


That would be more in-line with common UNIX practices, and unsurprising, familiar.



As a minor nitpick, I avoid quoting when it's unnecessary.
For example instead of [ "$p" != '//' ], I would write [ "$p" != // ]



Instead of this:


        [ -z "$p" ] &amp;&amp; p=/



This form is shorter:

        [ "$p" ] || p=/




This might be subjective, but I would rewrite this part using regular if-else:


[ -n "$skip78" ] || {
    { [ "$p" != '//' ] || [ -z "$alt" ]; } &amp;&amp; {
        stripslashes "$p" p
        [ -z "$p" ] &amp;&amp; p=/
    }
}