Do you realise how many times you type "cd" per day? Do you realise
how many times you retype the same directory names again and again?
Ever since I migrated from 4DOS/NT shell on Windows to using
Bash on Unix
platforms, I was missing its "cd" history access. In 4NT the history of
the visited directories can be navigated by Ctrl+PgUp/Dn. Every time
you go to a new directory by "cd", its name automaticly goes on top of
an easily accessible history list.
In Bash "cd -" switches between the last two directories. This is a
function in the right direction but many times I wanted to go to the
directory before the last, I dreamed for something like "cd -2".
Brief scripting restores some sanity in the directory navigation of
Bash.
To install the modified CD function, copy acd_func.sh
to your home directory. At the end of your .bashrc add "source
acd_func.sh". Restart your bash session and then type "cd --".
lotzmana@safe$ cd --
0 ~
Type "cd --" to verify if the installation works. Above you may
see the result "0 ~". This shows that you have one directory in your
history.
lotzmana@safe$ cd work
lotzmana@safe$ cd scripts
lotzmana@safe$ pwd
/home/petarma/work/scripts
lotzmana@safe$ cd --
0 ~/work/scripts
1 ~/work
2 ~
lotzmana@safe$ cd -2
lotzmana@safe$ pwd
/home/petarma
The "cd" command works as usual. The new feature is the history of
the last 10 directories and "cd" expanded to display and
access it. "cd --" (or simply pressing ctrl+w) shows the history. In
front of every directory name you see number. "cd -num" with the
number you want, jumps to the corresponding directory from the history.
lotzmana@safe$ nl -w2 -s' ' acd_func.sh
1 # do ". acd_func.sh"
2 # acd_func 1.0.5, 10-nov-2004
3 # petar marinov, http:/oocities.com/h2428, this is public domain
4 cd_func ()
5 {
6 local x2 the_new_dir adir index
7 local -i cnt
8 if [[ $1 == "--" ]]; then
9 dirs -v
10 return 0
11 fi
12 the_new_dir=$1
13 [[ -z $1 ]] && the_new_dir=$HOME
14 if [[ ${the_new_dir:0:1} == '-' ]]; then
15 #
16 # Extract dir N from dirs
17 index=${the_new_dir:1}
18 [[ -z $index ]] && index=1
19 adir=$(dirs +$index)
20 [[ -z $adir ]] && return 1
21 the_new_dir=$adir
22 fi
23 #
24 # '~' has to be substituted by ${HOME}
25 [[ ${the_new_dir:0:1} == '~' ]] && the_new_dir="${HOME}${the_new_dir:1}"
26 #
27 # Now change to the new dir and add to the top of the stack
28 pushd "${the_new_dir}" > /dev/null
29 [[ $? -ne 0 ]] && return 1
30 the_new_dir=$(pwd)
31 #
32 # Trim down everything beyond 11th entry
33 popd -n +11 2>/dev/null 1>/dev/null
34 #
35 # Remove any other occurence of this dir, skipping the top of the stack
36 for ((cnt=1; cnt <= 10; cnt++)); do
37 x2=$(dirs +${cnt} 2>/dev/null)
38 [[ $? -ne 0 ]] && return 0
39 [[ ${x2:0:1} == '~' ]] && x2="${HOME}${x2:1}"
40 if [[ "${x2}" == "${the_new_dir}" ]]; then
41 popd -n +$cnt 2>/dev/null 1>/dev/null
42 cnt=cnt-1
43 fi
44 done
45 return 0
46 }
47 alias cd=cd_func
48 if [[ $BASH_VERSION > "2.05a" ]]; then
49 # ctrl+w shows the menu
50 bind -x "\"\C-w\":cd_func -- ;"
51 fi
4-7: cd_func() is a function, variables are declared local and are automaticly deleted at the end of the function
8-11: if the function is called with a parameter "--" then it dumps
the current content of the directory history. It is stored in the same
place pushd/popd keep names -- the directory stack. Storage is the
same, access is different.
12-13: Argument $1 is transferred into $the_new_dir for some
postprocessing. Immediately after that, if there are no parameters we
assume that user asked for his home directory
14-22: If parameter begins with '-' then user attempts access to one
of the names in the history list. $index gets the number of the
directory, then into $adir we extract the correspondent name. For
example, "dirs +3" dumps directory #3 from the stack.
At this point in $the_new_dir we have either a name specified
explicitely as a parameter or a name obtained from the history of
previously visited directories.
23-25: If a directory name begins with '~' then this character has
to be replaced by the actual home directory name.
26-30: pushd does the actual CD. It also puts the name on top of the directory stack. stdout is redirected to /dev/null in order to completely imitate how CD works. Notice that any output to stderr, for example a message telling that the directory specified by the user doesn't exist will show up, which is again similar to what CD does. The function aborts if pushd fails. We also need the new directory name for further analysis and $the_new_dir carries it down the function.
31-33: Keeping track of more than 10 directories is unproductive. As
we have just pushd-ed one on top of the stack we trim down any that
fall below 11
names deep.
34-44: In a loop we walk through all the names in the directories
stack.
Any name that matches the new current directory is eliminated. Again,
we have to translate any name from the list which begins with '~' to
its format of fully expanded home directory.
47: We assign cd to be cd_func().
48-51: If bash version allowes for macros to be assigned we make
ctrl+w to summon the history of visited directories.
This script defines a function. It must be source-d and not
executed. This way cd_func() is parsed and stored in the current
environment. Try "env"
and you must see it after all environment variables.