From 2d0fefe4cde7831c7e49641df217ee0354751a63 Mon Sep 17 00:00:00 2001 From: Tom Ryder Date: Fri, 26 May 2017 20:24:13 +1200 Subject: Reimplement ud() More fault-tolerant and no subshell or temporary vars --- sh/shrc.d/ud.sh | 62 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 30 insertions(+), 32 deletions(-) (limited to 'sh') diff --git a/sh/shrc.d/ud.sh b/sh/shrc.d/ud.sh index 79f4b5e7..a3997702 100644 --- a/sh/shrc.d/ud.sh +++ b/sh/shrc.d/ud.sh @@ -2,48 +2,46 @@ # like cd .., cd ../.., etc ud() { - # Check argument count - if [ "$#" -gt 1 ] ; then + # Check arguments; default to 1 and $PWD + if [ "$#" -gt 2 ] ; then printf >&2 'ud(): Too many arguments\n' return 2 fi + set -- "${1:-1}" "${2:-"$PWD"}" + set -- "$1" "$2" - # Check first argument, number of steps upward, default to 1. - # "0" is weird, but valid; "-1" however makes no sense at all - if [ "${1:-1}" -lt 0 ] ; then + # Check first argument, number of steps upward. "0" is weird, but valid; + # "-1" however makes no sense at all + if [ "$1" -lt 0 ] ; then printf >&2 'ud(): Invalid step count\n' return 2 fi - # Change the positional parameters from the number of steps given to a - # "../../.." string - set -- "$( - - # Append /.. to the target (default PWD) the specified number of times - dirname=${2:-"$PWD"} - i=0 - steps=${1:-1} - while [ "$i" -lt "$steps" ] ; do - dirname=${dirname%/}/.. - i=$((i+1)) + # Check second argument, starting path, for relativity and anchor it if + # need be + case $2 in + /*) ;; + *) set -- "$1" "$PWD"/"$2" ;; + esac + + # Chop an element off the target the specified number of times + while [ "$1" -gt 0 ] ; do + + # Make certain there are no trailing slashes to foul us up + while : ; do + case $2 in + */) set -- "$1" "${2%/}" ;; + *) break ;; + esac done - # Check we have a target after all that - if [ -z "$dirname" ] ; then - printf >&2 'ud(): Destination construction failed\n' - exit 1 - fi + # Strip a path element + set -- "$(($1-1))" "${2%/*}" + done - # Print the target with trailing slash to work around newline stripping - printf '%s/' "${dirname%/}" - )" + # Shift off the count, which should now be zero + shift - # Remove trailing slash - set -- "${1%/}" - - # If the subshell printed nothing, return with failure - [ -n "$1" ] || return - - # Try to change into the determined directory - command cd -- "$@" + # Try to change into the determined directory, or the root if blank + command cd -- "${1:-/}" } -- cgit v1.2.3