From b4b144c3f8aa98a26b9a59c204ec0d5b4619ef72 Mon Sep 17 00:00:00 2001 From: Tom Ryder Date: Thu, 25 May 2017 18:21:23 +1200 Subject: Shorter/saner implementation for bd() Avoids subshell mess and consequent trailing-space workaround --- sh/shrc.d/bd.sh | 85 ++++++++++++++++++++------------------------------------- 1 file changed, 29 insertions(+), 56 deletions(-) diff --git a/sh/shrc.d/bd.sh b/sh/shrc.d/bd.sh index bf64a9aa..02da6773 100644 --- a/sh/shrc.d/bd.sh +++ b/sh/shrc.d/bd.sh @@ -3,68 +3,41 @@ bd() { # Check argument count if [ "$#" -gt 1 ] ; then - printf >&2 'bd(): Too many arguments' + printf >&2 'bd(): Too many arguments\n' return 2 fi - # Set positional parameters to an option terminator and what will hopefully - # end up being a target directory - set -- "$( + # Look at argument given + case $1 in - # The requested pattern is the first argument, defaulting to just the - # parent directory - req=${1:-..} + # If it has a leading slash or is . or .., don't touch the arguments + /*|.|..) ;; - # Strip trailing slashes if a trailing slash is not the whole pattern - [ "$req" = / ] || req=${req%/} + # Otherwise, we'll try to find a matching ancestor and then shift the + # initial request off the argument list + *) - # What to do now depends on the request - case $req in + # Push the current directory onto the stack + set -- "$1" "$PWD" - # Just go straight to the root or dot directories if asked - (/|.|..) - dirname=$req - ;; - - # Anything with a leading / needs to anchor to the start of the - # path. A strange request though. Why not just use cd? - (/*) - dirname=$req - case $PWD in - ("$dirname"/*) ;; - (*) dirname='' ;; + # Keep chopping at the current directory until it's empty or it + # matches the request + while [ -n "$2" ] ; do + set -- "$1" "${2%/*}" + case $2 in + (*/"$1") break ;; esac - ;; - - # In all other cases, iterate through the PWD to find a match, or - # whittle the target down to an empty string trying - (*) - dirname=$PWD - while [ -n "$dirname" ] ; do - dirname=${dirname%/*} - case $dirname in - (*/"$req") break ;; - esac - done - ;; - esac - - # Check we have a target after all that - if [ -z "$dirname" ] ; then - printf >&2 'bd(): Directory name not in path\n' - exit 1 - fi - - # Print the target with trailing slash to work around newline stripping - printf '%s/' "${dirname%/}" - )" - - # 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 -- "$@" + done + + # If the first argument ended up empty, we have no match + if [ -z "$2" ] ; then + printf >&2 'bd(): No match\n' + return 1 + fi + shift + ;; + esac + + # We have a match; try and change into it + command cd -- "$1" } -- cgit v1.2.3