News for hackers. Stuff that matters.
Re:Z – Jump Around by kazinator on Wednesday January 17, 2024 @06:06PM (#39034888)
It would be good to have a log of the last N directories visited in variables like cd1, cd2, cd3, ... cd9.

These could be interpolated into commands:

  $ cd somewhere # found via CDPATH
  /path/to/somewhere
  $ cp $cd1/file.txt .   # copy file from where we were just before the cd, to here
cd1 would be always the most recent, cd2 second most recent.

Here it is up to cd4:

  if [ -z "$c1" ] ; then
    c4=
    c3=
    c2=
  fi

  cdlog_chdir()
  {
    local cur=$PWD
    if command cd "$@" ; then
      c4=$c3
      c3=$c2
      c2=$c1
      c1=$cur
    fi
  }

  cdlog_chdir()
  {
    local cur=$PWD

    if command cd "$@" && [ "$PWD" != "$cur" ]; then
      # only if we successfully change to a different
      # directory do the following

      if [ "$cur" == "$c2" ] && [ "$PWD" == "$c1" ] ; then
        # Special case: if we changed to the directory that is second
        # in the cdlog, then just swap between those two.
        c2=$PWD
        c1=$cur
      else
        # Otherwise rotate through all four.
        c4=$c3
        c3=$c2
        c2=$c1
        c1=$cur
      fi
    fi
  }
Tip: in bash use ESC Ctrl-E to expand variables in the command line before running it. Then the command is recorded with concrete paths, rather than variables that are changing. You can recall the command from history to repeat it.

Handy aliases:

  alias cd='cdlog_chdir -P'
  alias cs='cdlog_chdir "$c1"'
"cs" goes back to the previous; if that is repeated, it triggers the swapping behavior in cdlog_chdir, hence the "cs" name.
Re:Z – Jump Around by Adj_and_Styles on Thursday January 18, 2024 @01:38AM (#39038700)
In bash you can probably use "pushd" instead of "cd" (it builds a stack and can swap top two entries, as well as rotate the stack) and abuse "dirs" (to create the variables with something like "c3=$( dirs +3 2>/dev/null )").
Re:Z – Jump Around by kazinator on Thursday January 18, 2024 @02:13AM (#39038908)
I've used the pushd stuff for 30 years, but this system I just came up with is way more ergonomic.

The dirs command is a poorly considered interface to what should just be a handful of simple variables.

The whole feature is basically mistake, copied from tcsh in order to help tcsh users switch to Bash or something.

pushd with no arguments swaps between the most recent two. That's an important use case and I nailed that requirement with the "cs" alias.

I just added a function to cdlog_pop aliased to pd, which changes to $c1 and removes it, rotating the entries in the other direction. I extended the history to 9 items (with c5 to c9 being hidden entries not shown by cdlog).

  if [ -z "$c1" ] ; then
    c9=
    c8=
    c7=
    c6=
    c5=
    c4=
    c3=
    c2=
  fi

  cdlog_chdir()
  {
    local cur=$PWD

    if command cd "$@" && [ "$PWD" != "$cur" ]; then
      # only if we successfully change to a different
      # directory do the following

      if [ "$cur" == "$c2" ] && [ "$PWD" == "$c1" ] ; then
        # Special case: if we changed to the directory that is second
        # in the cdlog, then just swap between those two.
        c2=$PWD
        c1=$cur
      else
        # Otherwise rotate through all
        c9=$c8
        c8=$c7
        c7=$c6
        c6=$c5
        c5=$c4
        c4=$c3
        c3=$c2
        c2=$c1
        c1=$cur
      fi
    fi
  }

  cdlog_pop()
  {
    if [ -n "$c1" ] && command cd "$c1"; then
      c1=$c2
      c2=$c3
      c3=$c4
      c4=$c5
      c5=$c6
      c6=$c7
      c7=$c8
      c8=$c9
      c9=
    fi
  }

  cdlog()
  {
    # c5 through c9 are hidden
    printf "c1: %s\n" "$c1"
    printf "c2: %s\n" "$c2"
    printf "c3: %s\n" "$c3"
    printf "c4: %s\n" "$c4"
  }

  alias cs='cdlog_chdir "$c1"'
  alias cd='cdlog_chdir -P'
  alias pd='cdlog_pop'
Re:Z – Jump Around by JNRowe on Thursday January 18, 2024 @05:48AM (#39040266)
pushd also gives you simple access via `~-n` in both bash and zsh out of the box. The bonus being that `~-[n]` is correctly handled when used as an argument to other commands too, so that `cp myfile ~-4/` does what you'd expect. The zsh completion for cd functions really nicely for this too, given that you can use zstyle to annotate your entries or group dynamic completions separately.

If you're using zsh you can implement the GPs idea by hooking chpwd, so that you don't need to change your muscle memory on cd. You can even combine them by enabling the auto_pushd option and just carrying on with regular cd whilst gaining access to `~-n` with no other changes.

Re:Z – Jump Around by kazinator on Thursday January 18, 2024 @08:15AM (#39041275)
~-2 is shorter than messing with the dirs command but it does not improve over $c2. It does not rescue the whole directory stack misfeature which has no reason for existing as built-in C code inside Bash, with dedicated expansion syntax. It interferes with existing syntax, too; according to POSIX, that refers to the home directory of a user named -2.

We can "export c1" and the others to make them visible to child processes. That makes it possible to launch a subshell in which the parent shell's directory log is readily available.

Things that can be very effectively and ergonomically achieved with a bit of scripting in the user's environment should never be built into the command interpreter. That just adds to the executable size, documentation, and maintenance.

Re:Z – Jump Around by JNRowe on Thursday January 18, 2024 @10:50AM (#39043209)
Personally, I'd find the completion output from `~-<Tab>` a worthy improvement, although admittedly with zsh you can configure completion to display both name and value for `$c<Tab>` too I guess(can't recall if that is possible with bash).

User names should not begin with a hyphen¹, so it shouldn't interfere with home directory references.

The child process point is an interesting one, I feel like you could workaround it by using a chpwd hook to export $dirstack and set it at startup(or a precmd function and $DIRSTACK if using bash). Something like `export DIRSTACK=${(pj#:#)dirstack}` in the hook and `dirstack=(${(ps#:#)DIRSTACK})` in the startup file. I'll concede this point though, as while a fun thought experiment the end result would be ghastly ;)

¹ https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1...

Re:Z – Jump Around by kazinator on Thursday January 18, 2024 @01:39PM (#39045756)
I'm noticing that in Bash, when I complete on ~-1 without a trailing slash, nothing happens. When I use $c1 and hit tab, it adds the slash for me! So in that situation, $c1 saves a keystroke. Well, not a keystroke per se, but having to use a different character rather than repeated Tab.

Also, I can shorten these variables to one letter, like $p, $q, ...

Bash has an option so that tab completion will expand out the directory.

  shopt -s direxpand
this is useful because variables like $c1 or syntax like ~-1 entered into the history is not useful for recall.

I know about M-x C-e to expand everything, but that is far too clumsy compared to just the tab completion doing it.

Here is something: the direxpand option is not working for ~-n/[Tab] completions. Only for variable names!

Re:Z – Jump Around by JNRowe on Friday January 19, 2024 @12:25AM (#39051856)
My description above wasn't great. I meant completion would be an improvement in the sense that under zsh `~-<Tab>` can show not only the possible completion options, but also the value they expand to. For example, mine is set to display in the manner of a colorful version of the text below:

    $ cd ~-
    0 -- ~
    1 -- ~/Downloads
    2 -- ~/Desktop
The handling of tilde expansion under bash is a readline option, so users would configure it in their inputrc. It is pretty useful in general, because then it is also available to tools like imv¹ beyond just bash. Unfortunately, as it is handled separately from word completion you'd need to change the completion function to make it match the mark-directories behaviour you like.

If I wanted the text expanded for history use I'd probably expand it before writing using a zshaddhistory hook, but I don't think there is an equivalent in bash so changing the line buffer probably is the best option.

¹ https://www.nongnu.org/renameutils/