pushd/popd on ksh?
Asked Answered
W

6

9

Is there an equivalent of the bash pushd/popd build in commands for the KSH?

For those who don't know what pushd and popd in bash do, here is the description from the man page

   pushd [-n] [dir]
   pushd [-n] [+n] [-n]
          Adds  a directory to the top of the directory stack, or rotates
          the stack, making the new top of the stack the current  working
          directory.   With  no arguments, exchanges the top two directo-
          ries and returns 0, unless the directory stack is empty.


   popd [-n] [+n] [-n]
          Removes entries from the directory stack.  With  no  arguments,
          removes  the top directory from the stack, and performs a cd to
          the new top directory.  Arguments, if supplied, have  the  fol-
          lowing meanings:

Thanks

Written answered 12/6, 2009 at 1:34 Comment(1)
An excellent pushd/popd installation appears here in one of Oracle's blogs - presumably relating to its use in Solaris.Cutinize
H
10

When I discovered that ksh didn't include these, I wrote my own. I put this in ~/bin/dirstack.ksh and my .kshrc file includes it like this:

. ~/bin/dirstack.ksh

Here are the contents of dirstack.ksh:

# Implement a csh-like directory stack in ksh
#
# environment variable dir_stack contains all directory entries except
# the current directory

unset dir_stack
export dir_stack


# Three forms of the pushd command:
#    pushd        - swap the top two stack entries
#    pushd +3     - swap top stack entry and entry 3 from top
#    pushd newdir - cd to newdir, creating new stack entry

function pushd
{
   sd=${#dir_stack[*]}  # get total stack depth
   if [ $1 ] ; then
      if [ ${1#\+[0-9]*} ] ; then
         # ======= "pushd dir" =======

         # is "dir" reachable?
         if [ `(cd $1) 2>/dev/null; echo $?` -ne 0 ] ; then
            cd $1               # get the actual shell error message
            return 1            # return complaint status
         fi

         # yes, we can reach the new directory; continue

         (( sd = sd + 1 ))      # stack gets one deeper
         dir_stack[sd]=$PWD
         cd $1
         # check for duplicate stack entries
         # current "top of stack" = ids; compare ids+dsdel to $PWD
         # either "ids" or "dsdel" must increment with each loop
         #
         (( ids = 1 ))          # loop from bottom of stack up
         (( dsdel = 0 ))        # no deleted entries yet
         while [ ids+dsdel -le sd ] ; do
            if [ "${dir_stack[ids+dsdel]}" = "$PWD" ] ; then
               (( dsdel = dsdel + 1 ))  # logically remove duplicate
            else
               if [ dsdel -gt 0 ] ; then        # copy down
                  dir_stack[ids]="${dir_stack[ids+dsdel]}"
               fi
               (( ids = ids + 1 ))
            fi
         done

         # delete any junk left at stack top (after deleting dups)

         while [ ids -le sd ] ; do
            unset dir_stack[ids]
            (( ids = ids + 1 ))
         done
         unset ids
         unset dsdel
      else
         # ======= "pushd +n" =======
         (( sd = sd + 1 - ${1#\+} ))    # Go 'n - 1' down from the stack top
         if [ sd -lt 1 ] ; then (( sd = 1 )) ; fi
         cd ${dir_stack[sd]}            # Swap stack top with +n position
         dir_stack[sd]=$OLDPWD
      fi
   else
      #    ======= "pushd" =======
      cd ${dir_stack[sd]}       # Swap stack top with +1 position
      dir_stack[sd]=$OLDPWD
   fi
}

function popd
{
   sd=${#dir_stack[*]}
   if [ $sd -gt 0 ] ; then
      cd ${dir_stack[sd]}
      unset dir_stack[sd]
   else
      cd ~
   fi
}

function dirs
{
   echo "0: $PWD"
   sd=${#dir_stack[*]}
   (( ind = 1 ))
   while [ $sd -gt 0 ]
   do
      echo "$ind: ${dir_stack[sd]}"
      (( sd = sd - 1 ))
      (( ind = ind + 1 ))
   done
}
Hestia answered 12/6, 2009 at 1:44 Comment(3)
This seems like the most promising response, I'll give a try after lunchWritten
I hope it works for you. I've been using it for over a decade. It does not exactly reproduce the behavior of the pushd/popd/dirs built into csh/bash, but it is very close.Hestia
Very nice. I've just found this today and will be setting it up as standard on the ksh systems that I work with. :)Copper
P
4

If you are OK with just a single level of back tracking you can alias 'cd -' or 'cd $OLDPWD' to popd.

As for dir.ksh... according to Google it's part of a commercial package:

NOTE

popd is a KornShell function defined in the file

$ROOTDIR/etc/dir.ksh.

This file is normally processed by a login shell during the processing of the file $ROOTDIR/etc/profile.ksh. If your system fails to recognize the popd command, check your profile.ksh file to ensure that a call to dir.ksh is included.

AVAILABILITY

MKS Toolkit for Power Users MKS Toolkit for System Administrators MKS Toolkit for Developers MKS Toolkit for Interoperability MKS Toolkit for Professional Developers MKS Toolkit for Enterprise Developers MKS Toolkit for Enterprise Developers 64-Bit Edition

Phenacetin answered 12/6, 2009 at 2:41 Comment(2)
I use cd - but sometimes I need more :)Written
The name dir.sh is generic enough that it could be all over the place. What's more, I don't think the MKS toolkit is around any more.Cutinize
B
2

I usually use a subshell for this sort of thing:

(cd tmp; echo "test" >tmpfile)

This changes to the tmp directory and creates a file called tmpfile in that directory. After the subshell returns, the current directory is restored to what it was before the subshell started. This is because each shell instance has its own idea of what the "current directory" is, and changing the current directory in a subshell does not affect the shell that called it.

Baltazar answered 12/6, 2009 at 1:38 Comment(0)
T
1

There's a subtle bug in the accepted answer. When 'pushd' is invoked with no arg ($1 = "") AND an empty dir_stack, it injects a blank entry into said stack that can't be "popped" out of. Catching the edge case seems to fix it.

Here's the corrected code. EDIT: complain to stderr when nowhere to push (consistent w/bash pushd). Left the indexed listing on 'dirs' as I feel it's an improvement :')

# Implement a csh-like directory stack in ksh
#
# environment variable dir_stack contains all directory entries except
# the current directory

unset dir_stack
export dir_stack


# Three forms of the pushd command:
#    pushd        - swap the top two stack entries
#    pushd +3     - swap top stack entry and entry 3 from top
#    pushd newdir - cd to newdir, creating new stack entry

function pushd
{
   sd=${#dir_stack[*]}  # get total stack depth
   if [ $1 ] ; then
      if [ ${1#\+[0-9]*} ] ; then
         # ======= "pushd dir" =======

         # is "dir" reachable?
         if [ `(cd $1) 2>/dev/null; echo $?` -ne 0 ] ; then
            cd $1               # get the actual shell error message
            return 1            # return complaint status
         fi

         # yes, we can reach the new directory; continue

         (( sd = sd + 1 ))      # stack gets one deeper
         dir_stack[sd]=$PWD
         cd $1
         # check for duplicate stack entries
         # current "top of stack" = ids; compare ids+dsdel to $PWD
         # either "ids" or "dsdel" must increment with each loop
         #
         (( ids = 1 ))          # loop from bottom of stack up
         (( dsdel = 0 ))        # no deleted entries yet
         while [ ids+dsdel -le sd ] ; do
            if [ "${dir_stack[ids+dsdel]}" = "$PWD" ] ; then
               (( dsdel = dsdel + 1 ))  # logically remove duplicate
            else
               if [ dsdel -gt 0 ] ; then        # copy down
                  dir_stack[ids]="${dir_stack[ids+dsdel]}"
               fi
               (( ids = ids + 1 ))
            fi
         done

         # delete any junk left at stack top (after deleting dups)

         while [ ids -le sd ] ; do
            unset dir_stack[ids]
            (( ids = ids + 1 ))
         done
         unset ids
         unset dsdel
      else
         # ======= "pushd +n" =======
         (( sd = sd + 1 - ${1#\+} ))    # Go 'n - 1' down from the stack top
         if [ sd -lt 1 ] ; then (( sd = 1 )) ; fi
         cd ${dir_stack[sd]}            # Swap stack top with +n position
         dir_stack[sd]=$OLDPWD
      fi
   else
      #    ======= "pushd" =======
      # swap only if there's a value to swap with
      if [ ${#dir_stack[*]} = "0" ]; then
         echo "ksh: pushd: no other directory" >&2
      else
         cd ${dir_stack[sd]}       # Swap stack top with +1 position
         dir_stack[sd]=$OLDPWD
      fi
   fi
}

function popd
{
   sd=${#dir_stack[*]}
   if [ $sd -gt 0 ] ; then
      cd ${dir_stack[sd]}
      unset dir_stack[sd]
   else
      cd ~
   fi
}

function dirs
{
   echo "0: $PWD"
   sd=${#dir_stack[*]}
   (( ind = 1 ))
   while [ $sd -gt 0 ]
   do
      echo "$ind: ${dir_stack[sd]}"
      (( sd = sd - 1 ))
      (( ind = ind + 1 ))
   done
}
Timelag answered 13/7, 2019 at 18:3 Comment(0)
L
0

If your system fails to recognize the pushd command, check your profile.ksh file to ensure that a call to dir.ksh is included.

Linc answered 12/6, 2009 at 1:40 Comment(6)
pushd is a shell function defined in the file $ROOTDIR/etc/dir.ksh.Linc
Not on any Solaris server I have handy. First, I don't have $ROOTDIR defined, and second, /etc does not have ANY *.ksh files in it.Hestia
And just to double-check, I checked a Solaris 7 and a Solaris 10 box, so I checked more than one version. Your dir.ksh must be locally defined, or perhaps you're not using Solaris. In that case, it's OS-specific, not machine specific.Hestia
exactly dir.ksh is not on my system eitherWritten
So it's probably OS specific as you said.Linc
It's probably installation specific: no UNIX or Linux OS I know of will have this as part of the installation. Linux usually uses bash which includes the popd/pushd as one of its features already; UNIX shells don't support this outside of C shell (csh).Cutinize
Y
0

The ksh93 community branch (u+m) has a fairly good implementation in its source code, although it is usually not sourced by default.

Yunyunfei answered 30/11, 2022 at 2:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.