aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Ryder <tom@sanctum.geek.nz>2016-08-19 12:48:35 +1200
committerTom Ryder <tom@sanctum.geek.nz>2016-08-19 12:48:35 +1200
commit3d8cf05dfc685e799aa5e20319e8e33288e74e04 (patch)
tree28668e9ba83860c7a30cea5a2c5b80533121ae39
parentFlag issue fixed (diff)
downloaddotfiles-3d8cf05dfc685e799aa5e20319e8e33288e74e04.tar.gz
dotfiles-3d8cf05dfc685e799aa5e20319e8e33288e74e04.zip
Change cd() into POSIX shell script
-rw-r--r--README.markdown4
-rw-r--r--bash/bashrc.d/cd.bash32
-rw-r--r--sh/shrc.d/cd.sh72
3 files changed, 74 insertions, 34 deletions
diff --git a/README.markdown b/README.markdown
index 2367aac9..b443d618 100644
--- a/README.markdown
+++ b/README.markdown
@@ -166,6 +166,8 @@ in `sh/shrc.d` to be loaded by any POSIX interactive shell. Those include:
* `bc()` silences startup messages from GNU `bc(1)`.
* `diff()` forces the unified format for `diff(1)`.
+* `cd()` wraps the `cd` builtin to allow for a second parameter for string
+ substitution, emulating a Zsh function I like.
* `ed()` tries to get verbose error messages, a prompt, and a Readline
environment for `ed(1)`.
* `env()` sorts the output of `env(1)` if it was invoked with no arguments,
@@ -193,8 +195,6 @@ There are a few other little tricks defined for other shells, mostly in
`bash/bashrc.d`:
* `bd()` changes into a named ancestor of the current directory.
-* `cd()` wraps the `cd` builtin to allow for a second parameter for string
- substitution, emulating a Zsh function I like.
* `fnl()` runs a command and saves its output and error into temporary files,
defining variables with the filenames in them.
* `grep()` tries to apply color and other options good for interactive use,
diff --git a/bash/bashrc.d/cd.bash b/bash/bashrc.d/cd.bash
deleted file mode 100644
index 04de96d5..00000000
--- a/bash/bashrc.d/cd.bash
+++ /dev/null
@@ -1,32 +0,0 @@
-# If given two arguments to cd, replace the first with the second in $PWD,
-# emulating a Zsh function that I often find useful; preserves options too
-cd() {
- local arg
- local -a opts
- for arg ; do
- case $arg in
- --)
- shift
- break
- ;;
- -*)
- shift
- opts[${#opts[@]}]=$arg
- ;;
- *)
- break
- ;;
- esac
- done
- if (($# == 2)) ; then
- if [[ $PWD == *"$1"* ]] ; then
- builtin cd "${opts[@]}" -- "${PWD/"$1"/"$2"}"
- else
- printf 'bash: %s: could not replace substring\n' \
- "$FUNCNAME" >&2
- return 2
- fi
- else
- builtin cd "${opts[@]}" -- "$@"
- fi
-}
diff --git a/sh/shrc.d/cd.sh b/sh/shrc.d/cd.sh
new file mode 100644
index 00000000..dd98a422
--- /dev/null
+++ b/sh/shrc.d/cd.sh
@@ -0,0 +1,72 @@
+# If given two arguments, replace the first instance of the first argument with
+# the second argument in $PWD, and make that the target of cd(). This POSIX
+# version cannot handle options, but it can handle an option terminator (--),
+# so e.g. `cd -- -foo -bar` should work.
+cd() {
+
+ # First check to see if we can perform the substitution at all
+ if (
+
+ # If we have any options, we can't do it, because POSIX shell doesn't
+ # let us (cleanly) save the list of options for use later in the script
+ for arg ; do
+ case $arg in
+ --) break ;;
+ -*) return 1 ;;
+ esac
+ done
+
+ # Shift off -- if it's the first argument
+ [ "$1" = -- ] && shift
+
+ # Check we have two non-null arguments
+ [ "$#" -eq 2 ] || return
+ [ -n "$1" ] || return
+ [ -n "$2" ] || return
+
+ ) ; then
+
+ # Set the positional parameters to an option terminator and what will
+ # hopefully end up being the substituted directory name
+ set -- -- "$(
+
+ # If the first of the existing positional arguments is --, shift it
+ # off
+ [ "$1" = -- ] && shift
+
+ # Current path: e.g. /foo/ayy/bar/ayy
+ cur=$PWD
+ # Pattern to replace: e.g. ayy
+ pat=$1
+ # Text with which to replace pattern: e.g. lmao
+ rep=$2
+
+ # /foo/
+ curtc=${cur%%"$pat"*}
+ # /bar/ayy
+ curlc=${cur#*"$pat"}
+ # /foo/lmao/bar/ayy
+ new=${curtc}${rep}${curlc}
+
+ # Check pattern was actually in $PWD; this indirectly checks that
+ # $PWD and $pat are both actually set, too; it's valid for $rep to
+ # be empty, though
+ [ "$cur" != "$curtc" ] || exit
+
+ # Check we ended up with something to change into
+ [ -n "$new" ] || exit
+
+ # Print the replaced result
+ printf '%s\n' "$new"
+ )"
+
+ # Check we have a second argument
+ if [ -z "$2" ] ; then
+ printf >&2 'cd(): Substitution failed\n'
+ return 1
+ fi
+ fi
+
+ # Execute the cd command as normal
+ command cd "$@"
+}