How to save the current working directory to Zsh history?
Asked Answered
B

4

8

I wanted to achieve the same as asked here Saving current directory to bash history but within Zsh shell. I haven't done any Zsh trickery before but so far I have:

function precmd {
  hpwd=$history[$((HISTCMD-1))]  
  if [[ $hpwd == "cd" ]]; then  
    cwd=$OLDPWD
  else
    cwd=$PWD
  fi
  hpwd="${hpwd% ### *} ### $cwd"
  echo "$hpwd" >>~/.hist_log
}

Right now I save the command annotated with the directory name to a log file. This works fine for me. Just thought there might be a way to make the same replacement in the history buffer itself.

Bravissimo answered 13/5, 2010 at 2:4 Comment(2)
possible duplicate of How can I (from a script) add something to the zsh command history?Randeerandel
You could consider an independent project that supports saving the path for each command: github.com/chrissound/MoscoviumOrangeSchliemann
H
8
function _-accept-line() {
    [[ -z "${BUFFER" ]] || [[ "${BUFFER}" =~ "### ${(q)PWD}\$" ]] || BUFFER="${BUFFER} ### ${PWD}"
    zle .accept-line
}
zle -N accept-line _-accept-line

Will add ### ${PWD} to your command line. Not the best solution you could use, but it works.

UPD: Answer based on @Dennis Williamson's comment:

function zshaddhistory() {
    print -sr "${1%%$'\n'} ### ${PWD}"
    fc -p
}
Heady answered 13/5, 2010 at 12:7 Comment(5)
Thanks Dennis and Zyx. It works as expected. Dennis, it is a duplicate. Somehow that question didn't show up during my search. Your article on bash/zsh history is helpfule and informative.Bravissimo
Can someone explain to me why fc -p is needed? Why is it that when I do not place fc -p here, the history list gains two entries for each command (the first being the one including the path, and the second entry is plain one)?Loy
I was struggling with zsh trying to parse the # characters, but switching on setopt interactivecomments addressed that and I am happy with this setup now. It bloats the command history a good bit as you traverse it though, which makes me sad.Loy
This has a problem with searching back history - all you entries still have the comments. If you just do ctrl-r, something, enter you get duplicated entries...Saturant
See Adam Cooper answer for better solutionCastora
M
3

Rather than store it on every command I added the following to the beginging of my precmd() function to store it when I change directories:

    if [ "$LAST_DIR" != "$PWD" ]
    then
            print -s "##dir## $PWD"
            LAST_DIR=$PWD
    fi

Adds a '##dir## dir name' standalone line to the history each time a command is run from a new directory.

Mungo answered 13/10, 2011 at 17:20 Comment(3)
I like print -s "$PWD # Path", this way I can bring up this history entry all normal-like, and it's ready for me to hit Enter to auto-cd to that path (this is a feature of zsh, if you just provide a path it cd/pushd's it for you). HOWEVER I do believe this approach is fundamentally flawed: If you have two terms open, each in different dir, then keep their respective dirs, but switch between them issuing commands, it is not possible to know which dir those commands happened in! This is fail :(Loy
@StevenLu: There's actually a built-in zsh function for when the PWD is changed: chpwd So I just added my code there. FYI - I used your idea of $PWD #Change WD instead of the entire line being a comment so that I can use the history entry to return to the relevant directory. I used 'Change WD' instead of 'Path' so that backwards history searches don't have so many false negatives. I use this in a Cygwin term on a windows machine where I only ever run the 1 terminal so your fail case doesn't bother me.Mungo
The thing I made back in 2013 was an "extended" history file of my own devising where the pwd of the command in question is always logged. It could be optimized to a log for only when it changes, as well, since I track the tty/pty in that history too. But capturing all the metadata all the time has been working out very well for me.Loy
G
2

The following adaptation of ZyX's solution resolves an issue whereby reusing history commands with ⍐ (UpArrow) leads to a pile-up of ### $PWD in the history:

function zshaddhistory() {
  history_item="${${1%%$'\n'}%%$' ###'*} ### ${PWD}"
  print -sr ${(z)history_item}
  fc -p
  return 1
}

It strips any existing PWD-comment before adding the current one. This way I can freely hit the UpArrow, execute an old command without backspacing through the comment, and the new history item will only have the one comment with the current working directory.

Gangland answered 7/4, 2021 at 13:37 Comment(0)
S
1

I modified zsh's source code; the history file like:

: 1685803508:/home/gogogo/abc/;tail -f ~/.histfile

don't change any previous things

diff --git a/Src/hist.c b/Src/hist.c
index bff0abe..9de1a3d 100644
--- a/Src/hist.c
+++ b/Src/hist.c
@@ -29,6 +29,7 @@
 
 #include "zsh.mdh"
 #include "hist.pro"
+#include <unistd.h>
 
 /* Functions to call for getting/ungetting a character and for history
  * word control. */
@@ -3027,9 +3028,11 @@ savehistfile(char *fn, int err, int writeflags)
                histfile_linect++;
            }
            t = start = he->node.nam;
+           char cwd[PATH_MAX];
+           getcwd(cwd, sizeof(cwd));
            if (extended_history) {
-               ret = fprintf(out, ": %ld:%ld;", (long)he->stim,
-                             he->ftim? (long)(he->ftim - he->stim) : 0L);
+               ret = fprintf(out, ": %ld:%s;", (long)he->stim, cwd);
+                             // he->ftim? (long)(he->ftim - he->stim) : 0L);
            } else if (*t == ':')
                ret = fputc('\\', out);
Saccharase answered 3/6, 2023 at 14:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.