diff options
author | Tom Ryder <tom@sanctum.geek.nz> | 2016-08-19 12:48:35 +1200 |
---|---|---|
committer | Tom Ryder <tom@sanctum.geek.nz> | 2016-08-19 12:48:35 +1200 |
commit | 3d8cf05dfc685e799aa5e20319e8e33288e74e04 (patch) | |
tree | 28668e9ba83860c7a30cea5a2c5b80533121ae39 | |
parent | Flag issue fixed (diff) | |
download | dotfiles-3d8cf05dfc685e799aa5e20319e8e33288e74e04.tar.gz dotfiles-3d8cf05dfc685e799aa5e20319e8e33288e74e04.zip |
Change cd() into POSIX shell script
-rw-r--r-- | README.markdown | 4 | ||||
-rw-r--r-- | bash/bashrc.d/cd.bash | 32 | ||||
-rw-r--r-- | sh/shrc.d/cd.sh | 72 |
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 "$@" +} |