aboutsummaryrefslogtreecommitdiff
path: root/bash/bashrc.d/ud.bash
blob: b05539aba8f3b437009a60e71df90989d77f0525 (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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# Shortcut to step up the directory tree with an arbitrary number of steps,
# like cd .., cd ../.., etc
ud() {

    # For completeness' sake, we'll pass any options to cd
    local arg
    local -a opts
    for arg ; do
        case $arg in
            --)
                shift
                break
                ;;
            -*)
                shift
                opts=("${opts[@]}" "$arg")
                ;;
            *)
                break
                ;;
        esac
    done

    # Check and save optional first argument, number of steps upward; default
    # to 1 if absent
    local -i steps
    steps=${1:-1}
    if ! ((steps > 0)) ; then
        printf 'bash: %s: Invalid step count %s\n' "$FUNCNAME" "$1" >&2
        return 2
    fi

    # Check and save optional second argument, target directory; default to
    # $PWD (typical usage case)
    local dirname
    dirname=${2:-$PWD}
    if [[ ! -e $dirname ]] ; then
        printf 'bash: %s: Target directory %s does not exist\n' "$FUNCNAME" "$2" >&2
        return 1
    fi

    # Append /.. to the target the specified number of times
    local -i i
    for (( i = 0 ; i < steps ; i++ )) ; do
        dirname=${dirname%/}/..
    done

    # Try to change into it
    cd "${opts[@]}" -- "$dirname"
}

# Completion setup for ud
_ud() {

    # Only makes sense for the second argument
    ((COMP_CWORD == 2)) || return 1

    # Iterate through directories, null-separated, add them to completions
    local dirname
    while IFS= read -d '' -r dirname ; do
        COMPREPLY=("${COMPREPLY[@]}" "$dirname")
    done < <(

        # Set options to glob correctly
        shopt -s dotglob nullglob

        # Collect directory names, strip trailing slashes
        local -a dirnames
        dirnames=("${COMP_WORDS[COMP_CWORD]}"*/)
        dirnames=("${dirnames[@]%/}")

        # Bail if no results to prevent empty output
        ((${#dirnames[@]})) || exit 1

        # Print results null-delimited
        printf '%s\0' "${dirnames[@]}"
    )
}
complete -F _ud -o filenames ud