C shell pushd/popd on steroids as Korn Shell functions

Blogfinger inspires me to post some of my crazy KSH function code. The code below is a heavily hacked version of a similar functions that I got from Will several years ago. Someday I should post my partial ASN.1 DER encoder/decoder written in KSH :)

Syntax highlighting courtesy of VIM and its :TOhtml feature.

typeset|grep '^integer cdx$' > /dev/null || integer cdx=0
typeset|grep '^integer cdsx$' > /dev/null || integer cdsx=0
function cdhelp
{
cat <<-"EOF"
        Usage: cdinit  [<file>]
        Usage: cdcl
            Initialize directory list and stack.
            Clear directory list and stack.
        Usage: cdfind  <path> [quiet]
        Usage: cdgrep  <partial path> [first]
            Find a directory, by exact match in the cd list.
            Find matching directories in the cd list.
        Usage: cdls
        Usage: cdrf    [-a|--append] [<files>|-]
            Show the cd list. Use this to save your cd list.
            Read in a cd list and replace the current cd list.
        Usage: cdsv    [<directory paths>] (if none given then CWD)
            Save the given or current directory to the cd list.
        Usage: cdow    <index>
            Overwrite the given entry in the cd list with the CWD.
        Usage: cdto    <index>|<path>|[+]<partial path>
            Change the current directory to an entry from the cd list,
            or, if a path is given, then change to the given path and
            save to the cd list.
        Usage: cdrm [<path>|<index>]
            Remove a directory, or the current directory from the cdlist.
        Usage: cdsort
            Sort the cd list (alphanumerically)
        Usage: pushd   [<directory>]
        Usage: popd    [<number>]
        Usage: rightd  [<number>] (reverse of popd)
        Usage: dirs    (shows dirs in pushd/popd stack)
            Directory stack, similar to the C-Shell built-ins of similar names.
EOF
}
function cdfind
{
typeset p dir
integer i
if [[ $# -lt 1 || -z "$1" ]]
then
print "Usage: cdfind <path> [quiet]"
return 5
fi
i=0
p="$1"
[[ "$1" != /* ]] && p="$PWD/$1"
for dir in "${cdlist[@]}"
do
if [[ "$p" = "$dir" ]]
then
if [[ "$2" != quiet ]]
then
print "Found at index $i"
return 0
fi
return $i
fi
i=i+1
done
return 255
}
function cdgrep
{
typeset i s
integer i=0 s=1
if [[ $# -lt 1 || $# -gt 2 || -z "$1" ]]
then
print "Usage: cdgrep <partial path> [first]"
fi
while [[ i -lt cdx ]]
do
if eval "[[ \"${cdlist[i]}\" = *${1}* ]]"
then
print "$i ${cdlist[i]}"
s=0
[[ "$2" = first ]] && return 0
fi
i=i+1
done
return $s
}
function cdshow
{
typeset found_at
cdfind "$PWD" quiet
found_at=$?
[[ $found_at -eq 255 ]] && found_at='[unsaved]'
print "$found_at $PWD"
return 0
}
function cdto
{
typeset i j dir
integer i
if [[ $# -ne 1 || -z "$1" ]]
then
print "Usage: cdto <index>|<path>|[+]<partial path>"
fi
if [[ "$1" = +([0-9]) ]]
then
if cd "${cdlist[$1]}"
then
print "$1 ${cdlist[$1]}"
return 0
fi
else
if [[ "$1" != \+* && -d "$1" ]]
then
if cd "$1"
then
cdsv > /dev/null
cdshow
return 0
fi
fi
cdgrep "${1#\+}" first|read j dir
if [[ -d "$dir" ]]
then
if cd "$dir"
then
cdsv > /dev/null
cdshow
return 0
fi
fi
fi
print "Could not cd to $1"
return 1
}
function cdls
{
integer i=0
while [[ i -lt cdx ]]
do
print $i ${cdlist[i]}
i=i+1
done
}
cdcl ()
{
cdx=0
cdsx=0
unset cdlist
unset cdstack
set -A cdlist
set -A cdstack
}
function cdsv
{
typeset dir current
integer i
if [[ "$1" = -h || "$1" = --help ]]
then
print "Usage: cdsv [<directory paths>] (if none given then CWD)"
return 1
fi
# Look for $PWD in cdlist[]
current=""
if [[ $# -eq 0 ]]
then
current="current "
set -- "$PWD"
fi
for dir in "$@"
do
cdfind "$dir" quiet
i=$?
if [[ $i -ne 255 ]]
then
print "The ${current}directory $dir is already in the cdlist ($i)"
continue
fi
#cdlist[${#cdlist[@]}]="$PWD"
cdlist[cdx]=$PWD
cdx=cdx+1
done
}
# overwrite entry
function cdow
{
integer i
if [[ $# -ne 1 || "$1" != +([0-9]) ]];
then
print "Usage: cdow index#(see cdls)"
return 1
fi
cdfind "$PWD" quiet
i=$?
if [[ "$1" -gt ${#cdlist[@]} ]]
then
print "Index is beyond cdlist end ($1 > ${#cdlist[@]})"
return 1
fi
if [[ $i -ne 255 ]]
then
print "The current directory is already in the cdlist ($i)"
return 1
fi
cdlist[$1]=$PWD
return 0
}
function cdrf
{
typeset f status dir spath
typeset cdx_copy cdlist_copy usage
integer cdx_copy=0
usage="Usage: cdrf [-a|--append] [<files>|-]"
status=1
set -A cdlist_copy --
# Options
while [[ $# -gt 0 && "$1" = -?* ]]
do
case "$1" in
-s|--strip)
spath=$2
shift
;;
-a|--append)
cdx_copy=$cdx
set -A cdlist_copy -- "${cdlist[@]}"
;;
*)  print "$usage"
return 1
;;
esac
shift
done
# Default to reading stdin
[[ $# -eq 0 ]] && set -- -
# Process cdlist files
for f in "$@"
do
if [[ "$f" = - ]]
then
# STDIN
sed -e 's/^[0-9]* //' | while read dir
do
dir=${dir#$spath}
[[ "$dir" != /* ]] && dir="$PWD/$dir"
cdlist_copy[cdx_copy]=${dir}
cdx_copy=cdx_copy+1
done
status=0
continue
fi
# Find the cdlist file
if [[ ! -f "$f" && ! -f "$HOME/.cdpath.$f" ]]
then
print "No such cdpath file $p or $HOME/.cdpath.$f"
continue
fi
[[ ! -f "$f" && -f "$HOME/.cdpath.$f" ]] && f="$HOME/.cdpath.$f"
# Read the cdlist file
sed -e 's/^[0-9]* //' "$f" | while read dir
do
dir=${dir#$spath}
[[ "$dir" != /* ]] && dir="$PWD/$dir"
cdlist_copy[cdx_copy]=${dir}
cdx_copy=cdx_copy+1
done
status=0
done
[[ $status -ne 0 ]] && return $status
# Install new cdlist
cdcl
cdx=$cdx_copy
set -A cdlist -- "${cdlist_copy[@]}"
return 0
}
function cdsort
{
cdls | sed -e 's/^[0-9]* //' | sort -u | cdrf -
cdls
}
function cdrm
{
integer i
if [[ -n "$1" && "$1" != +([0-9]) ]]
then
cdfind "${1:-$PWD}" quiet
i=$?
elif [[ -n "$1" && "$1" = +([0-9]) ]]
then
i=$1
elif [[ $# -ne 0 ]]
then
print "Usage: cdrm <path>|<index>"
return 1
else
cdfind "${1:-$PWD}" quiet
i=$?
fi
if [[ "$i" -eq 255 ]] && return 1
then
cdfind "${1:-$PWD}"
fi
cdls|grep -v "^${i} "|sed -e 's/^[0-9]* //'|cdrf
i=$?
cdls
return $i
}
function pushd
{
if [[ $# -gt 0 && ! -d "$1" ]]
then
print "Can't cd to: $1"
return 1
fi
cdstack[$cdsx]="$PWD"
if cd "${1:-.}"
then
cdsx=cdsx+1
cdstack[cdsx]="$PWD"
[[ "$2" = sv || "$2" = save ]] && cdsv
cdshow
else
print "Could not cd to $1"
return 1
fi
return 0
}
function pushdsv
{
pushd "$1" save
}
function popd
{
if [[ $((cdsx-${1:-1})) -lt 0 || $((${#cdstack[@]} - ${1:-1})) -lt 0 ]]
then
print "Empty stack or popping too much"
return 1
fi
if [[ ! -d "${cdstack[cdsx-${1:-1}]}" ]]
then
print "Can't cd to: ${cdstack[cdsx-${1:-1}]}"
return 2
fi
if cd "${cdstack[cdsx-1]}"
then
cdsx=cdsx-1
cdshow
else
print "Could not popd to ${cdstack[cdsx-1]}"
return 1
fi
return 0
}
function dirs
{
integer i
if [[ ${#cdstack[@]} -eq 0 ]]
then
print "Empty stack"
return 1
fi
i=1
print -n "${cdstack[0]}"
while [[ $i -le $((cdsx)) && $i -le ${#cdstack[@]} ]]
do
print -n " ${cdstack[i]}"
i=i+1
done
if [[ ${#cdstack[@]} -gt $cdsx && $i -lt ${#cdstack[@]} ]]
then
print -n " <-> "
while [[ $i -lt ${#cdstack[@]} ]]
do
print -n " ${cdstack[i]}"
i=i+1
done
fi
print
return 1
}
function rightd
{
typeset i
integer i
if [[ -n "$1" && "$1" != +([0-9]) ]]
then
print "Usage: rightd [<number>]"
return 1
fi
i=${1:-1}
if [[ ${#cdstack[@]} -le $((cdsx+i)) ]]
then
print "No directories to the right on the stack"
return 1
fi
if [[ ! -d "${cdstack[cdsx+i]}" ]]
then
print "Can't cd to: ${cdstack[cdsx+i]}"
return 2
fi
if cd "${cdstack[cdsx+i]}"
then
cdsx=cdsx+i
cdshow
else
print "Could not cd to ${cdstack[cdsx+i]}"
return 1
fi
return 0
}
cdinit ()
{
[[ -n "${recd}" ]] && return 0
cdcl
if [[ $# -eq 0 && -f ~/lib/cdpaths ]]
then
cdrf < ~/lib/cdpaths
elif [[ $# -eq 1 ]]
then
cdrf "$1"
fi
cdls
}
vicd ()
{
${EDITOR:-vi} ~/.kshcd
}
recd ()
{
typeset recd
recd=inprogress
 . ~/.kshcd
recd=""
}
cdinit

~ by nico on April 5, 2007.

One Response to “C shell pushd/popd on steroids as Korn Shell functions”

  1. Nicolas,
    great stuff! Thanks!
    It inspired me to a nice “mkdir && cd” function I wanted to have since ages, but never searched for or programmed – in my blog entry now.

    cu,
    Bernd

Leave a Reply

Your email address will not be published. Required fields are marked *