Bash functions for path manipulation

Here is a set of path manipulation functions that I first wrote in 2000, and have tinkered with on and off since then.

The functions:
The file is available online.

# N.B  The following PATH manipulation functions
# 1) handle the empty path "", but do not equate the empty path with "."
# 2) will fail on all rem_* functions if
#    ALL of the characters '|' '%' ',' '~' '#' '@'
#    appear in the path being manipulated.
#    The chk-separator() function will check for the presence of each character in turn
#    until it finds one that does not occur.  If all occur, the rem_* functions will not
#    attempt removal.

# These functions have been written to cope with spaces in the file paths.

# Check each possible regular expression delimiter in turn against the first argument.
# Set the variable dlim to the first delimiter that does NOT occur, or "" if all occur.
chk_delim() {
    local c z
    if [ $# -lt 1 ]; then return; fi
    for c in '|' '%' ',' '~' '#' '@'
    do
        z=`expr "$1" : '.*'"$c"'.*'`
        if [ $z -eq 0 ]; then dlim=$c; break; fi
    done
    if [ "$c" != "$dlim" ]
    then
        dlim=""
    fi
}

# Clear path element given as arg from PATH
# N.B. path element MUST NOT contain a vertical bar `|'
rem_path() {
    if [ $# -lt 1 ]; then return; fi
    rem_vpath PATH "$@"
}

# Clear path element(s) given as arg 2 (3 ...) from the path named in arg1
# N.B. path element MUST NOT contain a vertical bar `|'
rem_vpath() {
    local pathvalue pathname rem_el dlim tmp_path
    if [ $# -lt 2 ]; then return; fi
    eval pathvalue=\"\$$1\"
    chk_delim "$pathvalue"
    # If existing pathvalue cannot be edited, return immediately
    if [ "$dlim" == "" ]; then return; fi
    pathname=$1
    shift
    while [ $# -ge 1 ]; do
        rem_el="$1"
        shift
        tmp_path="$pathvalue""$rem_el"
        chk_delim "$tmp_path"
        if [ "$dlim" == "" ]; then continue; fi
        pathvalue=`echo $pathvalue|
        sed ':loop
{s'"$dlim"':'"$rem_el"':'"$dlim"':'"$dlim"'g
t loop
}
s'"$dlim"'^'"$rem_el"':'"$dlim$dlim"'
s'"$dlim"':'"$rem_el"'$'"$dlim$dlim"'
s'"$dlim"'^'"$rem_el"'$'"$dlim$dlim"''`
    done
    eval $pathname=\"$pathvalue\"
}

# To path named in $1, append path element arg(s), replacing all existing
# instances in the named path
# N.B. path elements MUST NOT contain a vertical bar `|'
app_vpath() {
    local pathname pathvalue el
    pathname=$1; shift
    for el
    do
        rem_vpath $pathname "$el"
        eval pathvalue=\"\$$pathname\"
        pathvalue="${pathvalue:+${pathvalue}:}$el"
        eval ${pathname}=\"$pathvalue\"
    done
    export $pathname
}

# Append path element(s) given as args to PATH, replacing all existing
# instances in the PATH
# N.B. path elements MUST NOT contain a vertical bar `|'
app_path() {
    app_vpath PATH "$@"
}

# To path named in $1, prepend path element arg(s), replacing all existing
# instances in the named path.  Elements will be appear in the PATH in
# argument order.
# N.B. path elements MUST NOT contain a vertical bar `|'
pre_vpath() {
    local pathname pathvalue sptr element
    pathname=$1; shift
    sptr=0
    while [ $# -gt 0 ]
    do
        eval local elstack$((++sptr))=\"\$1\"
        shift
    done
    while [ $sptr -gt 0 ]
    do
        eval element=\$\{elstack$((sptr--))\}
        rem_vpath $pathname "$element"
        eval pathvalue=\"\$$pathname\"
        pathvalue="$element${pathvalue:+:${pathvalue}}"
        eval ${pathname}=\"$pathvalue\"
    done
    export $pathname
}

# Prepend path element(s) given as args to PATH, replacing all existing
# instances in the PATH.  N.B. elements will be appear in the PATH in
# REVERSE argument order.
# N.B. path elements MUST NOT contain a vertical bar `|'
pre_path() {
    pre_vpath PATH "$@"
}


# Clean a path - run pre_vpath with every current element of the path
# in reverse order
# This removes all duplicates in the path, and leaves the first instance
# of a path element in its original relative place - later ones are deleted.
clean_vpath() {
    local pathname pathvalue
    pathname=$1; shift
    # pathname contains the name of the path
    eval pathvalue=\"\$$pathname\"
    # pathvalue contains the value of the path
    pathvalue=`echo "$pathvalue"|sed 's/^/"/
s/:/" "/g
s/$/"/'`
    eval pre_vpath $pathname "$pathvalue"
}

clean_path() {
    clean_vpath PATH
}

# This prints out the elements of the path named in its first argument,
# one per line.
list_vpath() {
    if [ $# -lt 1 ]; then return; fi
    local pathname pathvalue
    pathname=$1; shift
    eval pathvalue=\"\$$pathname\"
    echo "$pathvalue"|sed ':loop
{h
s/:.*//
p
g
s/[^:]*://
t loop
d
}'
}

# This prints the elements of PATH, one per line
list_path() {
    list_vpath PATH
}

[ -n "$BASH" ] && \
export -f chk_delim rem_path rem_vpath app_path app_vpath pre_path pre_vpath clean_path clean_vpath list_path list_vpath