diff options
-rw-r--r-- | README.markdown | 2 | ||||
-rw-r--r-- | bash/bashrc.d/sd.bash | 109 | ||||
-rw-r--r-- | sh/shrc.d/sd.sh | 82 |
3 files changed, 83 insertions, 110 deletions
diff --git a/README.markdown b/README.markdown index 9bbf12d9..e9e04201 100644 --- a/README.markdown +++ b/README.markdown @@ -185,6 +185,7 @@ in `sh/shrc.d` to be loaded by any POSIX interactive shell. Those include: * `rcsdiff()` forces a unified format for `rcsdiff(1)`. * `scp()` tries to detect forgotten hostnames in `scp(1)` command calls. * `scr()` creates a temporary directory and changes into it. +* `sd()` changes into a sibling of the current directory. * `sudo()` forces `-H` for `sudo(8)` calls so that `$HOME` is never preserved; I hate ending up `root`-owned files in my home directory. * `tmux()` changes the default command for `tmux(1)` to `attach-session -d` @@ -211,7 +212,6 @@ There are a few other little tricks defined for other shells, mostly in * `pushd()` adds a default destination of `$HOME` to the `pushd` builtin. * `readv()` prints names and values from `read` calls to `stderr`. * `readz()` is an alias for `read -d '' -r`. -* `sd()` changes into a sibling of the current directory. * `vared()` allows interactively editing a variable with Readline, emulating a Zsh function I like by the same name. * `vr()` tries to change to the root directory of a source control diff --git a/bash/bashrc.d/sd.bash b/bash/bashrc.d/sd.bash deleted file mode 100644 index ad4a0deb..00000000 --- a/bash/bashrc.d/sd.bash +++ /dev/null @@ -1,109 +0,0 @@ -# -# sd -- sibling/switch directory -- Shortcut to switch to another directory -# with the same parent, i.e. a sibling of the current directory. -# -# $ pwd -# /home/you -# $ sd friend -# $ pwd -# /home/friend -# $ sd you -# $ pwd -# /home/you -# -# If no arguments are given and there's only one other sibling, switch to that; -# nice way to quickly toggle between two siblings. -# -# $ cd -- "$(mktemp -d)" -# $ pwd -# /tmp/tmp.ZSunna5Eup -# $ mkdir a b -# $ ls -# a b -# $ cd a -# pwd -# /tmp/tmp.ZSunna5Eup/a -# $ sd -# $ pwd -# /tmp/tmp.ZSunna5Eup/b -# $ sd -# $ pwd -# /tmp/tmp.ZSunna5Eup/a -# -# Seems to work for symbolic links. -# -sd() { - - # 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 - - # Set up local variable for the sibling to which we'll attempt to move, - # assuming we find one - local dirname - - # If we have one argument, it's easy, we just try to move to that one - if (($# == 1)) ; then - dirname=$1 - - # If no argument, the user is lazy; if there's only one sibling, we'll do - # what they mean and switch to it - elif (($# == 0)) ; then - - # This subshell switches on globbing functions to try to find all the - # current directory's siblings; it exits non-zero if it found anything - # other than one - if ! dirname=$( - shopt -s dotglob extglob nullglob - local -a siblings - - # Generate relative paths of all siblings - siblings=(../!("${PWD##*/}")/) - - # Strip the trailing slash - siblings=("${siblings[@]%/}") - - # Strip everything up to the basename - siblings=("${siblings[@]##*/}") - - # If some number of siblings besides one, exit non-zero - if ((${#siblings[@]} != 1)) ; then - exit 1 - fi - - # Otherwise, just print it - printf %s "${siblings[0]}" - - # This block is run if the subshell fails due to there not being a - # single sibling - ) ; then - printf 'bash: %s: No single sibling directory\n' \ - "$FUNCNAME" >&2 - return 1 - fi - - # Any other number of arguments is a usage error; say so - else - printf 'bash: %s: usage: %s [DIR]\n' \ - "$FUNCNAME" "$FUNCNAME" >&2 - return 2 - fi - - # Try to change into the determined directory - builtin cd "${opts[@]}" ../"$dirname" -} diff --git a/sh/shrc.d/sd.sh b/sh/shrc.d/sd.sh new file mode 100644 index 00000000..c6513aac --- /dev/null +++ b/sh/shrc.d/sd.sh @@ -0,0 +1,82 @@ +# +# sd -- sibling/switch directory -- Shortcut to switch to another directory +# with the same parent, i.e. a sibling of the current directory. +# +# $ pwd +# /home/you +# $ sd friend +# $ pwd +# /home/friend +# $ sd you +# $ pwd +# /home/you +# +# If no arguments are given and there's only one other sibling, switch to that; +# nice way to quickly toggle between two siblings. +# +# $ cd -- "$(mktemp -d)" +# $ pwd +# /tmp/tmp.ZSunna5Eup +# $ mkdir a b +# $ ls +# a b +# $ cd a +# pwd +# /tmp/tmp.ZSunna5Eup/a +# $ sd +# $ pwd +# /tmp/tmp.ZSunna5Eup/b +# $ sd +# $ pwd +# /tmp/tmp.ZSunna5Eup/a +# +# Seems to work for symbolic links. +# +sd() { + + set -- "$( + + # Check argument count + if [ "$#" -gt 1 ] ; then + printf >&2 'sd(): Too many arguments\n' + exit 1 + fi + + # Set the positional parameters to either the requested directory, or + # all of the current directory's siblings if no request + spec=$1 + set -- + if [ -n "$spec" ] ; then + set -- "$@" ../"$spec" + else + for sib in ../.* ../* ; do + case ${sib#../} in + .|..|"${PWD##*/}") continue ;; + esac + set -- "$@" "$sib" + done + fi + + # We should have exactly one sibling + case $# in + 1) ;; + 0) + printf >&2 'sd(): No siblings\n' + exit 1 + ;; + *) + printf >&2 'sd(): More than one sibling\n' + exit 1 + ;; + esac + + # Print the target + printf '%s\n' "$1" + )" + + # If the subshell printed nothing, return with failure + [ -n "$1" ] || return + + # Try to change into the determined directory + command cd -- "$@" +} |