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" ]; } && {
stripslashes "$p" p
[ -z "$p" ] && p=/
}
}
printf -- %s\\n "$p"
The POSIX spec for dirname
is:
If string is
//
, skip steps 2 to 5.If string consists entirely of <slash> characters, string shall be set to a single <slash> character. In this case, skip steps 3 to 8.
If there are any trailing <slash> characters in string, they shall be removed.
If there are no <slash> characters remaining in string, string shall be set to a single <period> character. In this case, skip steps 5 to 8.
If there are any trailing non- <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 <slash> characters in string, they shall be removed.
If the remaining string is empty, string shall be set to a single <slash> character.
Update: Made a gist.
dirname
. So anything that needs a sub-shell mostly negates the purpose. In theory this script would be turned into a function likestripslashes
that operates on its arguments. \$\endgroup\$ – Etan Reisner Sep 4 '15 at 14:09