-
-
Save jlintz/875554 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# c - bash directory changing functions that maintain a | |
# most-recently used stack. | |
# | |
# Run with -help as an argument to see invocation options, or find the | |
# d_usage function below. | |
# | |
# To use these functions, store this in a file someplace and | |
# then execute | |
# | |
# . filename | |
# | |
# where 'filename' is the file you stored this stuff in. You can then use c | |
# to change directories instead of cd. You'll probably the above line into | |
# your ~/.bash_profile if you like what these functions do for you. | |
# | |
# There's undoubtedly many shortcomings in the following. Changing dirs can | |
# be complicated. E.g., this will surely break if you have directories | |
# with whitespace in their names. That may not be too hard to fix, but I | |
# never do that, so I haven't worried about it. Sorry. | |
# | |
# Make sure you have a version of expr that knows how to do regex matches. | |
# Either via expr match str regex or expr str : regex. The code below uses | |
# the : form as it's older, and works on the expr that ships with Mac OS X | |
# and Ubuntu. If your expr can't handle : matches, get hold of the latest | |
# GNU sh-utils package and install that. It's a good idea to install it | |
# anyway, since some stock versions of expr are broken and will cause the | |
# below functions to break occasionally (e.g., Solaris 2.5.1 /usr/bin/expr | |
# gives a syntax error on expr match /usr / ). | |
# | |
# Terry Jones ([email protected]) | |
# Jan 2, 1998 (Modified Mar 17, 2011) | |
# | |
d_trunc_def=10 | |
d_version=1.02 | |
d_date='Jan 3, 1998' | |
function c() { | |
case $# in | |
0) | |
case "$d_last_call_time" in | |
'') | |
d_last_call_time=$SECONDS | |
d_show_dirs | |
return | |
;; | |
esac | |
local s | |
declare -i s=$SECONDS | |
if [ $[ $d_last_call_time + 1 ] -ge $s ] | |
then | |
# It's a double-d | |
d_last_call_time=$s | |
case "$my_dir_names" in | |
'') | |
my_dir_names="$HOME" | |
builtin cd | |
return | |
;; | |
esac | |
d_cd_match $HOME 1 | |
return | |
fi | |
d_show_dirs | |
d_last_call_time=$s | |
;; | |
1) | |
case $1 in | |
-) | |
d_cd_n 2 | |
return | |
;; | |
-c) | |
my_old_dir_names="$my_dir_names" | |
my_dir_names= | |
return | |
;; | |
-r) | |
my_dir_names="$my_old_dir_names" | |
d_show_dirs | |
return | |
;; | |
-s) | |
d_sort_dirs | |
d_show_dirs | |
return | |
;; | |
-t) | |
d_limit_n $d_trunc_def | |
d_show_dirs | |
return | |
;; | |
-v | -version | --version) | |
echo "d version $d_version ($d_date)" | |
return | |
;; | |
-help | --help) | |
d_usage | |
return | |
;; | |
.) | |
d_cd_match $PWD 1 | |
return | |
;; | |
..) | |
case "$my_dir_names" in | |
'') | |
my_dir_names=`dirname $PWD` | |
builtin cd .. | |
return | |
;; | |
esac | |
set $my_dir_names | |
d_cd_match `dirname $1` 1 | |
return | |
;; | |
[0-9]*) | |
d_cd_n $1 | |
return | |
;; | |
*) | |
local target=$1 | |
case $target in | |
/*) ;; | |
*) | |
if [ -d $target ] | |
then | |
target=$PWD/$target | |
fi | |
;; | |
esac | |
case "$my_dir_names" in | |
'') | |
my_dir_names="$target" | |
builtin cd $1 | |
return | |
;; | |
esac | |
d_cd_match $target 0 | |
return | |
;; | |
esac | |
;; | |
2) | |
case $1 in | |
-k) | |
case $2 in | |
[0-9]*) d_remove_n $2 ;; | |
*) d_kill_match $2 0 ;; | |
esac | |
;; | |
-t) | |
case $2 in | |
[0-9]*) | |
d_limit_n $2 | |
d_show_dirs | |
return | |
;; | |
*) | |
d_usage | |
return 1 | |
;; | |
esac | |
;; | |
*) | |
d_usage | |
return 1 | |
;; | |
esac | |
;; | |
*) | |
d_usage | |
return 1 | |
;; | |
esac | |
} | |
d_usage () { | |
local d=c | |
cat <<EOF | |
usage: $d [option] [. | - | dir | number | pattern] | |
$d (no args) = show dir stack. Twice in a row (quickly) takes you to \$HOME. | |
$d NUM = go to directory number NUM, and bring it to the stack top. | |
$d . = put the current directory at the top of the stack. | |
$d - = go to the previous directory in the stack. | |
$d PATTERN = go to the first directory in stack matching PATTERN. | |
$d DIR = go to directory DIR, and put it first on the stack. | |
$d -c = clear the directory stack. | |
$d -k NUM = kill (remove) directory number NUM in the stack. | |
$d -k PATTERN = kill (remove) the first directory in stack matching PATTERN. | |
$d -r = restore the directory stack to the last pre-clear state. | |
$d -s = sort the directory stack. | |
$d -t [NUM] = truncate the stack to top NUM elements (default $d_trunc_def). | |
EOF | |
} | |
d_cd_n () { | |
case "$my_dir_names" in | |
'') echo 'No $my_dir_names directories are set'; return;; | |
esac | |
local want | |
declare -i want=$1 | |
set $my_dir_names | |
if [ $want -gt $# ] | |
then | |
echo "We only have $# dirs in \$my_dir_names" | |
return | |
fi | |
local save= | |
while [ $want -gt 1 ] | |
do | |
save="$save $1" | |
shift | |
want="$want - 1" | |
done | |
local goto=$1 | |
shift | |
my_dir_names="$goto $save $*" | |
echo $goto | |
builtin cd $goto | |
} | |
d_show_dirs () { | |
case "$my_dir_names" in | |
'') return;; | |
esac | |
local i | |
declare -i i=1 | |
set $my_dir_names | |
while [ $# -ne 0 ] | |
do | |
echo "$i) $1" | |
i="$i + 1" | |
shift | |
done | |
} | |
d_cd_match () { | |
# Precondition: $my_dir_names is not empty | |
local target=$1 | |
local exact=$2 | |
set $my_dir_names | |
if [ $1 = $target ] | |
then | |
# We're there already. | |
return | |
fi | |
local pre= | |
local post= | |
case $exact in | |
0) pre=.\\\* ;; | |
1) post=\\\$ ;; | |
esac | |
shift # We ignore the first dir, since we're in that dir already. | |
local n | |
declare -i n=2 | |
while [ $# -ne 0 ] | |
do | |
case `eval expr $1 : $pre$target$post` in | |
0) | |
shift | |
n="$n + 1" | |
;; | |
*) | |
d_cd_n $n | |
return | |
;; | |
esac | |
done | |
my_dir_names="$target $my_dir_names" | |
builtin cd $target | |
} | |
d_kill_match () { | |
case "$my_dir_names" in | |
'') echo 'No $my_dir_names directories are set'; return;; | |
esac | |
local target=$1 | |
local exact=$2 | |
set $my_dir_names | |
local pre= | |
local post= | |
case $exact in | |
0) pre=.\* ;; | |
1) post=\$ ;; | |
esac | |
local n | |
declare -i n=1 | |
while [ $# -ne 0 ] | |
do | |
case `expr $1 : $pre$target$post` in | |
0) | |
shift | |
n="$n + 1" | |
;; | |
*) | |
d_remove_n $n | |
return | |
;; | |
esac | |
done | |
} | |
d_remove_n () { | |
# Precondition: $my_dir_names is not empty | |
local n | |
local save= | |
declare -i n=$1 | |
set $my_dir_names | |
while [ $n -gt 1 ] | |
do | |
save="$save $1" | |
shift | |
n="$n - 1" | |
done | |
shift | |
my_dir_names="$save $*" | |
d_show_dirs | |
} | |
d_limit_n () { | |
local n | |
local save= | |
declare -i n=$1 | |
case "$my_dir_names" in | |
'') return ;; | |
esac | |
set $my_dir_names | |
if [ $# -le $n ] | |
then | |
return | |
fi | |
while [ $n -gt 0 -a $# -gt 0 ] | |
do | |
save="$save $1" | |
shift | |
n="$n - 1" | |
done | |
my_dir_names="$save" | |
} | |
d_sort_dirs () { | |
case "$my_dir_names" in | |
'') return ;; | |
esac | |
local tmp=/tmp/d_$$_ | |
rm -f $tmp || return | |
set $my_dir_names | |
while [ $# -gt 0 ] | |
do | |
echo "$1" | |
shift | |
done | sort > $tmp | |
my_dir_names="`cat $tmp`" | |
rm -f $tmp | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment