Wrote This BASH function many years ago, while I felt frustrated and sometimes upset having to input same directory names repeatedly. Among all BASH scripts I've written, this one is probably the most useful and frequently needed. Below is the code. Put it here so it might benefit others who also consider
cd -
and
cd ~
is not capable enough for daily routines.
### functions
cdh()
{
### eliminate non-existing directories
if [ ${#CDHIST[@]} -ne 0 ]; then
declare -a tmp
for (( i = 0; i < ${#CDHIST[@]}; ++i )); do
if [ -d "${CDHIST[i]}" ]; then
tmp=("${tmp[@]}" "${CDHIST[i]}")
fi
done
CDHIST=("${tmp[@]}")
unset tmp
fi
### parse arguments
OPTIND=1; # this is necessary for any function intending to use the builtin command getopts.
typeset opt pl destination
while getopts PLla0123456789 opt; do
case "$opt" in
'l' | 'a')
if [ ${#CDHIST[@]} -eq 0 ]; then # this CDHIST not typeset-ed is in the global scope.
echo 'Error: The history is empty!' >&2
return 1
fi
typeset -i i=${#CDHIST[@]}
# print the contents of the CDHIST array
while [ "$i" -gt 0 ]; do
: $(( --i ))
printf "%d: %s\n" $i "${CDHIST[i]}"
done
# fall down or not
if [ "$opt" = 'l' ]; then
return 0
fi
# ask for a choice
read -p "$PS2" -n 1 choice
echo
# when the user opts not to change directory
if [ "$choice" = 'q' ]; then
return 0
fi
# what to do when there isn't any correct character or there's typo having been inputted.
if echo "$choice" | 'grep' --silent '^[0-9]\{1,\}$'; then # single-quote grep to avoid aliasing
:
else
echo 'Error: Integer value expected!' >&2
return 2
fi
# how to deal the "out of range" situation
if [ "$choice" -ge ${#CDHIST[@]} ]; then
echo 'Error: Out or range!' >&2
return 3
fi
# if nothing went wrong
destination="${CDHIST[$choice]}"
;;
[0-9])
# exception handling
if [ ${#CDHIST[@]} -eq 0 ]; then
echo 'Error: The history is empty!' >&2
return 4
fi
if [ $opt -ge ${#CDHIST[@]} ]; then
echo 'Error: Out or range!' >&2
return 5
fi
# decide the destination where cd is about to change to
destination="${CDHIST[$opt]}"
;;
'P' | 'L')
if [ -n "$pl" ]; then # there could be only one -P or -L, not both.
echo 'Error: Incorrect options!' >&2
return 6
fi
pl="$opt"
;;
*)
echo "Error: Unknown option $opt" >&2
return 7
;;
esac
done
unset opt
# decide what value the variable $destination shall be, if it still has no value.
shift $((OPTIND - 1))
if [ -z "$destination" -a "$#" -gt 0 ]; then
eval destination="\$$#"
fi
### use shell builtin command cd to change current working directory
if [ -z "$pl" ]; then
if [ -z "$destination" ]; then
'cd'
else
'cd' "$destination"
fi
else
'cd' -"$pl" "$destination"
fi # Note: The exit status of shell builtin cd should be checked immediately.
if [ $? -ne 0 -o "$OLDPWD" = "$PWD" ]; then # for weird operations including the specified directory not existing
unset pl destination
return 8; # don't update array CDHIST. leave it intact.
fi
unset pl destination
### update array CDHIST
# assign a appropriate value to the variable $top, which should be the index of an array element going to be deleted.
typeset -i i=0 top
while [ $i -lt ${#CDHIST[@]} ]; do
if [ "${CDHIST[i]}" = "${PWD}" ]; then
top=$i # inundate the duplicated array item
break
fi
: $((++i))
done
# initialize iterator $i
if [ $top ]; then # in this if-else statement, we're going to recycle variable $i to save the precious system resource.
i=$top
else
if [ ${#CDHIST[@]} -lt 10 ]; then # the capacity of array CDHIST is decided by this if-else statement.
i=${#CDHIST[@]}
else
i=9
fi
fi
unset top
# update array CDHIST
until [ $i -lt 1 ]; do
CDHIST[i]=${CDHIST[i - 1]} # overwrite array element no longer needed
: $((--i))
done
CDHIST[0]="$OLDPWD"
}
### aliases
alias cd=cdh
Save this in system file /etc/bashrc or your personal ~/.bashrc, just make sure it will be source-ed. The usage is simple.
cd -l
lists the directories you recently visited, and
cd -a
asks you which directory you wanna change to.
No comments:
Post a Comment