I found code I wrote for bash in the past, modified it for the POSIX shell, and added some miscellaneous tests. (Still) experimental implementation. May need more testing.
See also example.sh
Usage: path_dirname varname pathname
varname: variable name
pathname: path name
path_dirname ret "/var/tmp/file.txt"
echo "$ret" # => /var/tmp
Usage: path_basename varname pathname [suffix]
varname: variable name
pathname: path name
suffix: .*, .*.*, .**, .ext (default: NULL)
path_basename ret "/var/tmp/file.txt"
echo "$ret" # => file.txt
Usage: path_extname varname pathname [suffix]
varname: variable name
pathname: path name
suffix: .*, .*.*, .**, .ext (default: .*)
path_extname ret "/var/tmp/file.txt"
echo "$ret" # => .txt
suffix examples
path_extname ret "/var/tmp/file.tar.gz" .gz
echo "$ret" # => .gz
path_extname ret "/var/tmp/file.tar.gz" .tar.gz
echo "$ret" # => .tar.gz
path_extname ret "/var/tmp/file.tar.gz" ".*.*"
echo "$ret" # => .tar.gz
path_extname ret "/var/tmp/file.tar.gz" ".**"
echo "$ret" # => .tar.gz
Usage: path_rootname varname pathname [suffix]
varname: variable name
pathname: path name
suffix: .*, .*.*, .**, .ext (default: .*)
path_rootname ret "/var/tmp/file.txt"
echo "$ret" # => /var/tmp/file
Usage: path_normalize varname pathname [basedir]
varname: variable name
pathname: path name
basedir: base directory (default: PWD)
path_normalize ret "/var///tmp/.././file.txt"
echo "$ret" # => /var/file.txt
Usage: path_relative varname pathname [basedir]
varname: variable name
pathname: path name
basedir: base directory (default: PWD)
path_relative ret "/var/tmp/file.txt" /var/
echo "$ret" # => ./tmp/file.txt