aboutsummaryrefslogtreecommitdiff
path: root/sh/shrc.d/bd.sh
blob: 5593ff4d5206401d2afce4879b756bb94827f1da (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# Move back up the directory tree to the first directory matching the name
bd() {

    # Set positional parameters to an option terminator and what will hopefully
    # end up being a target directory
    set -- "$(

        # Check there's no more than one argument
        [ "$#" -le 1 ] || exit 1

        # The requested pattern is the first argument, defaulting to just the
        # parent directory
        req=${1:-..}

        # Strip trailing slashes if a trailing slash isn't the whole pattern
        [ "$req" = / ] || req=${req%/}

        # What to do now depends on the request
        case $req in

            # 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='' ;;
                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
        printf '%s\n' "$dirname"

    # If the subshell failed, return from the function with the same exit value
    )" || return

    # Try to change into the determined directory
    command cd -- "$@"
}