Here is a zsh function (added to ~/.zshrc) I made for use with https://github.com/larkery/zsh-histdb to get the same effect, but only showing commands whose exit_status is 0:
jog() {
sqlite3 $HOME/.histdb/zsh-history.db "
SELECT
replace(commands.argv, '
', '
')
FROM commands
JOIN history ON history.command_id = commands.id
JOIN places ON history.place_id = places.id
WHERE history.exit_status = 0
AND dir = '${PWD}'
AND places.host = '${HOST}'
AND commands.argv != 'jog'
AND commands.argv NOT LIKE 'z %'
AND commands.argv NOT LIKE 'cd %'
AND commands.argv != '..'
ORDER BY start_time DESC
LIMIT 10
"
}
I love having stuff like this in zsh except that I very often have commands that don't exit with 0 either because I made a mistake, or because of something happening like referencing a file that might have been there but wasn't.
I understand not wanting to clog up your history with typos, but some configs even default to not being able to press up-arrow to get a command that I just mistyped.
The function lrobinovitch showed is not overwriting your history, it just shows the successful commands when running `jog`. Your up/down-arrow and ctrl+p will still work like normal, typos or not.
But if you did do a mistake in your command, like referring to a file that doesn't exist, don't you rerun the command again but with the correct parameters? Then it would be included there as well (when running `jog`).
Otherwise, it's trivial to remove the WHERE statement so it shows all the commands, seems zsh-histdb stores everything, successful or not.
Indeed not. In fact the issue would be with some history never getting recorded at all. Thank you for clarifying though, that the dir-based history tools don't affect the existing history.
The example I was trying to explain above referred to deliberately referencing a filename which may not correspond to an existing file. Like I might stat a file, to know when it was created, but if it hasn't been created yet then that's an answer too.
Are you referring to the "Note for OS X users" section in the README? If so, it's only additional step, so 4 in total... I know macOS users might not be the most technical, but I think most of them (at least developers on macOS) should be able to follow it :)
btw my comment was not here to tell people I was lazy. It was here as feedback. Users are lazy. Online businesses track and look to decrease the number of clicks that lead to a sale, there's no reason developer tools shouldn't try to do the same :)
The other tools mentioned are all good, theres also jiq, which lets you run jq interactively against an input until you get the incantation right, then confirm it to get the full output.
I've posted this here in a comment before, but I just made some small updates and figured I'd submit. A while back I wished I could see what commands I'd run in a directory I hadn't worked in for months. I found a couple projects that could accomplish this but consisted of thousands of lines of code and a database install. I wanted a more "unix" solution so I built this.
If you are using bash, here's an alternative to save the commands, their start time, stop time, and eventual error code, in a SQLite database that you can query with fzy: https://github.com/csdvrx/bash-timestamping-sqlite
With the mappings, Ctrl-R will do a search history restricted to the current directory AND to successful execution (return == 0), while Ctrl-T will do a global history search for all directories and return values.
EDIT: to answer a question I've seen posted below, if you want to only navigate the commands from the current directory, do Ctrl-R then use the arrows: the command are by default sorted by the time they where previously run. If you then start typing letters, it will filter these commands using fzy.
IPython has a sqlite-backed history too. I manage to corrupt it frequently, presumably due to segfaults in extensions I'm developing, or because I rarely have fewer than 10 sessions open. I don't mind occasionally restarting IPython and very rarely nuking its history... but if that happened to me in bash, I'd be beyond frustrated. Is a non-concurrent database really a good idea here?
I always respect when people take the time to put a small but useful thing like this up in a repo by itself.
It has always been a frustration that the current working directory metadata isn't saved along with history entries, so you can never really reconstruct what was run.
Edit: I should really try out https://github.com/larkery/zsh-histdb. There's no reason a modern system's history shouldn't store start and finish timestamps, return code, current working directory, etc.
> It has always been a frustration that the current working directory metadata isn't saved along with history entries, so you can never really reconstruct what was run.
I'm a Fish inside Iterm2 user, constantly with a four panel split screen, and I'm always slightly amazed that _something_ seems to even remember which pane I ran various commands in. I have no idea which is remembering though.
That's a cool idea. I have a similar, but less elegant setup: I just append every single command that I type to a global (synchronized between machines) history file, one file per month, then I use fzf to search through these when I need something (which is plugged into the usual ^R shell binding). Every command I've typed over the past couple of years is accessible immediately that way.
It's not scoped by directory though, but I'm not sure I'd personally need that feature.
Came here to write something along the lines of "useless for me, because I hardly ever use cd at all in order to get a history containing lots of useful full path references". But you're right, something modern could be far more elegant.
(but it won't happen, these days it's crazy rare to "dig in" in a dazzlingly customized environment, we'd rather tweak our skills to work on the widest range of default environments. Certainly not the best conditions for innovation)
> these days it's crazy rare to "dig in" in a dazzlingly customized environment
Speak for yourself. The time I spend working locally is vastly more than I spend ssh-ing into somewhere vanilla so it's worth making my environment as comfortable and full-featured as possible.
Edit:
> I hardly ever use cd… to get a history [with] path references
That’s one way of solving the problem, but then you miss out on great tools like direnv… and relative paths.
> It has always been a frustration that the current working directory metadata isn't saved along with history entries, so you can never really reconstruct what was run.
Yes, underrated idea. I recently kept a spreadsheet of commands I'd ran, which branch, and where the output went. It would be great to see this tracked.
I keep shell sessions logged to individual files, collected for years back. I often reuse past commands. But if the command run from a git folder it's hard to tell which branch. It's also important to use full path names.
Zsh-histdb is one of my absolute lifesavers. It's literally the first thing I install on new systems, after zsh itself and oh-my-zsh. I could never go back to working without it.
Thanks I'll check this out! Any other useful zsh plugins you want to call out? It's been my favorite shell for a while mostly because of OMZ but always interested in some new magic!
This is one of the things that Fish shell gets right out of the box. Command autocomplete is per-directory, and shows you a preview of the commands as you type.
I had thought that too, but, interestingly enough, this doesn't seem to be the case. ~/.local/share/fish/fish_history does not include the cwd.
In fact it seems like Fish does something better: when adding to the history, it sees if any of the arguments to the command are valid file paths. These are stored separately. Fish will then autocomplete the command if detected paths are valid.
So for example if you run "git log somefile", whenever "somefile" is in your cwd you will get an autocompletion for that command.
I scrolled down looking for someone mentioning Ctrl-R. I rarely write down shell command lines, even very complex ones, since I usually can quickly (very quickly) access them from my shell history with just a few keystrokes after Ctrl-R. It is the single most useful and important tool in my shell toolbox.
It also obviates the need for many scripts and aliases I would otherwise write: Command lines can be (practically) arbitrarily long, and zsh at least has good multiline edit functionality (including just spawning an editor if you really need to). So there's a lot of "impromptu scripts", sometimes with different variations, in my shell's history.
Some other features I would not want to miss (one from zsh, the other also available in bash, I think), albeit less critical:
1. Alt-. (Alt dot) immediately recalls the last argument passed to the last command line. I use this very very often. "mkdir long_name" followed by "cd long_name" is just one example.
2. Alt-q makes the currently typed command line disappear. But the next time you press enter to get a new prompt, even if you have entered a different command line, it will reappear. Super useful if you remember that you want to do something else before completing your command, without opening another terminal (some things like changing the current directory or env variables can't even be done in another terminal). This can even be done multiple times, it's a stack.
Both need Alt behaving as "Meta" (a checkbox in macOS's Terminal.app for example), otherwise you can still type "ESC" followed by "." or "q", but that's two separate keystrokes.
Alt-. is in bash. I use it dozens of times per day, and its best feature is pressing it repeatedly to get arguments from earlier commands. Also if you press Alt-<number> before it, e.g. Alt-2, it inserts argument 2 (or whatever) from the previous command (0-indexed).
Alt-q isn't in bash, at least by default, but it sounds pretty useful. I accomplish a similar thing with Alt-#, call the (now commented) command back with Up after however many other commands, then Alt-3 Alt-# will uncomment and run it (any number works there, but 3 is on the same key as #.
For bash (without an sqlite database), you can add your cwd to your history file with:
export PROMPT_COMMAND='hpwd=$(history 1); hpwd="${hpwd# *[0-9]* }"; if [[ ${hpwd%% *} == "cd" ]]; then cwd=$OLDPWD; else cwd=$PWD; fi; hpwd="${hpwd% ### *} ### $cwd"; history -s "$hpwd"'
Then you can grep your history for your CWD.
This is a one line version of the following:
hpwd=$(history 1) # grab the most recent command
hpwd="${hpwd# *[0-9]* }" # strip off the history line number
if [[ ${hpwd%% *} == "cd" ]] # if it's a cd command, we want the old directory
then # so the comment matches other commands "where *were\* you when this was done?"
cwd=$OLDPWD
else
cwd=$PWD
fi
hpwd="${hpwd% ### \*} ### $cwd" # strip off the old ### comment if there was one so they
# don't accumulate, then build the comment
history -s "$hpwd" # replace the most recent command with itself plus the comment
Using fzf to power ^R (reverse history search) is such a huge productivity win. Also it has the side effect of showing you the last 10 commands without any other config or setup.
By default that doesn't seem to pay attention to the current directory or the directories commands were run in. Is that a configuration option or something?
No but it would be very straightforward to do something like what the OP does, and then instead of using tail you just pipe the grep result for PWD into fzf. Which has some advantages: all commands instead of last n, plus of course the killer feature which is fuzzy matching.
I'm a fan of McFly, which tracks history across directories and drives Ctrl-R with it. Although it can be a bit non-deterministic, it does help really work better than grepping history.
function cwd() {
cd "$1" || return
if [ -w "${PWD}/.bash_history" ]; then
history -a
history -c
HISTFILE="${PWD}/.bash_history"
history -r
fi
if [ -r "${PWD}/.bashrc" ]; then
source "${PWD}/.bashrc"
fi
}
Yes, a contextual history is very valuable, but the context isn't necessarily tied to the directory I'm currently in. Like git does as well, I'd want an entire subtree to share the same history. I'm sure that there's more elegant solutions for it than having to explicitly use cwd instead of cd to change contexts, but so far it's served me well.
Also, another way to find past commands you run `history | grep <whatever-you-are-looking-for>` and works wonders; or as other users have mentioned, Ctrl-R works too.
I'm not sure I love littering the filesystem with dotfiles (it gives me svn PTSD). Although the advantage is that it'll stay in the directory if it's renamed/moved/compressed etc...
But then the inconvenient is that it'll stay in the directory if it's renamed/moved/compressed etc... Better be careful if you tend to input sensitive data on the command line.
Very cool! I built something in python a while back, to track each project's history by detecting if a virtual environment/venv is active: https://github.com/haraball/pystory
I am unsure how shell history works when there are many terminals open at once. The history within a single shell is not mixed up by this, but I have never learned what happens when the terminals closes. I often wondered what would be the ideal way for history to work with many shells.
I use a script to name each GNU screen session "projectA", "sysadmin", etc.
Then in my .bash_profile, I set the Bash history to be a separate file in my ~/.bash_hist_dir subdirectory.
if [ "x$SCREEN_SESSION_NAME" != "x" -a "x$WINDOW" != "x" ]; then
if [ ! -d ~/.bash_hist_dir ]; then
mkdir ~/.bash_hist_dir
fi
export HISTFILE=~/.bash_hist_dir/${SCREEN_SESSION_NAME}.${WINDOW}
fi
So each separate window in each session gets a unique history file.
I have up to 10 windows per GNU screen session, and sometimes 6 to 8 terminal tabs.
Each separate GNU screen window is used for specific things: "edit", "build", "debug", etc.
This all makes it easy to maintain the context of all the things I work on.
This is cool but it really feels like this should be just one thing you can do easily, because you are storing your shell history in a relational database. As I’ve been meaning to start doing for ages, so maybe this will spur me.
This is such a great idea, but what's even more useful would be to make arrow keys only traverse the commands ran in the current directory. Would there be any prior art?
fzf[0] + per-directory-history[1] fill exactly this niche for me. ^R always shows the all commands run in the current directory, ordered by recency (and filterable).