aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Ryder <tom@sanctum.geek.nz>2016-08-20 14:15:58 +1200
committerTom Ryder <tom@sanctum.geek.nz>2016-08-20 14:19:38 +1200
commit7e10222deeb3f53bebc6514ddfd6321a4ac4c894 (patch)
tree469dee5d6cf2e5d74616b955a23f5d7b18b1ab82
parentPort pd() to POSIX sh (diff)
downloaddotfiles-7e10222deeb3f53bebc6514ddfd6321a4ac4c894.tar.gz
dotfiles-7e10222deeb3f53bebc6514ddfd6321a4ac4c894.zip
Port sd() to POSIX sh
-rw-r--r--README.markdown2
-rw-r--r--bash/bashrc.d/sd.bash109
-rw-r--r--sh/shrc.d/sd.sh82
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 -- "$@"
+}