Better Bash History (2012)
sanctum.geek.nz
sanctum.geek.nz
I find this super-helpful to remind me of the context for projects – cd in, then up-arrow to whatever that command is I usually run to launch this server, grep this logfile, etc.
There are some downsides – sometimes I want to run the same command in directory X and also in subdirectory X/Y, but I love doing it this way.
Anyone else done something similar?
Specifically, I'm not sure what "this" is that fish does, as I described interaction of multiple pieces of software and I don't think fish includes a terminal multiplexer?
The history file is set here: https://github.com/dlthomas/config-files/blob/master/.bash.d...
Thus, all of the `cd` commands that get written to my history use absolute paths and are re-usable no matter where I am.
It would be cool if this sort of functionality were generalizable to all commands where you type a relative path, not just `cd`.
https://github.com/dasl-/settings/blob/3143bbfe23bd75c3e35c7...
This isn't specific to `cd` commands either. It's useful if you are `rsync`ing different directories to different places, or checkout different `git` branches, etc.
Like, for example, would it handle this properly?
$ (cd /source;tar -cf - .) | (cd /dest;tar -xf -)
My particular configuration is at https://github.com/YenTheFirst/dotfiles/blob/master/.bash_cd...
90% of the time, the bash history is super relevant to the current context. A small amount of the time, I remember that I issued a command, but not in which directory - in these cases, I can just search the history directory, and get both the context of what the command was, and also where I was when I originally issued it.
https://github.com/oilshell/oil/issues/320
It seems like a weird hack but apparently it works for tons of things!
[1] Latest status: Success With the Interactive Shell http://www.oilshell.org/blog/2019/02/05.html . Oil runs a bunch of interactive programs like bash-completion, virtualenv, and git-prompt.
I keep an infinite log by appending a timestamp to the beginning of the lines written to my history file, then merging this into a growing log with an hourly cronjob that runs sort -n | uniq on the file and the new version of the shell history. This way the shell history file is just a buffer. It doesn't get too big to cause corruption or other issues with reverse search on the command line. I just grep my way through the big history file to figure out what I was doing and when.
The good thing is that it includes context: current directory, session, date.
I use it together with ssh-suggestions which can use SQL queries to find the most relevant command. You can program rules such as: use the most frequent command in that session, if you none do it for the current directory in the last 6 months, if not, search globally.
I'm not, but now that you mention this, I think I will. I already use direnv all over the place, and this would be a pretty natural extension. Thanks!
function log_history {
echo "`date '+%Y-%m-%d %H:%M:%S'` $HOSTNAME:$$ $PWD ($?) `history 1`" >> $MYHISTFILE
}
which records a timestamp, host name, shell pid, working directory and exit status for each command.Basically a container let's you wrap a part of your filesystem, network, process space in a separate world of its own. And in that world you have only the context related stuff like history, processes, libs etc. Additionally most container environments come with tooling that enables you to make everything reproducable, so that you can share your container with others.
The great thing is you can always live in global space, while switching between what that global space means.
You can implement some or all of the tips in this HN post, it'll make fzf results better.
A couple of plugs for your consideration:
- I integrated all of these history options in my project Sensible Bash [1], an attempt at saner Bash defaults
- I made an ebook [2] out of a series which appeared on this blog, titled “Unix as IDE”.
promptFunc() {
# right before prompting for the
# next command, save the previous
# command in a file.
echo "$(date +%Y-%m-%d--%H-%M-%S) \
$(hostname) $PWD $(history 1)" \
>> ~/.full_history
}
PROMPT_COMMAND=promptFunc
This is in addition to standard bash history, and is much more reliable. Being able to see not only what command I ran but what directory I was in and when I ran it is very useful.(I wrote about this here: https://www.jefftk.com/p/you-should-be-logging-shell-history)
Every developer should do this.
Edit: went to check and apparently I've overcomplicated this somewhat (it's about ten years old) but it is nice if empty lines are not logged. It's an old habit to ctrl-c then hit return repeatedly if a terminal is not responding.
PROMPT_COMMAND=storehist
storehist ()
{
CMDCNT=`history 1 | sed -e 's/\w* *\(.*\)/\1/'`;
if [ "$CMDCNT" != "$LASTCMDIND" ] && [ -n "$LASTCMDIND" ]; then
DATE=`date '+%Y%b%d %H%M'`;
if [ "$LASTCMDIND" != "$CMDCNT" ]; then
echo "$DATE $HOSTNAME:$BASHTTY $CMDCNT" >> ~/.custom_history;
fi;
LASTCMDIND="$CMDCNT";
fi;
LASTCMDIND="$CMDCNT"
}Every combination of history commands in bash didn't do the right thing[1], but Zsh's works as desired.
[1] eg. putting 'history -a' in PROMPT_COMMAND as the article recommends appends your session's entire history each time, IIRC.
No, only the last command. Very handy because that way you can start a new terminal and have your current history available immediately. I also have an alias r='history -n'. With this you can load the history of another terminal session directly into the current session.
Edit: now I'm obsessing about reproducing this behavior from years ago! If anyone knows what I'm talking about I'd be grateful for some validation :)
The unique thing is that I use a [python script (in my dotfiles)][1] to clean the history in a more advanced way than is possible with HISTIGNORE.
The script removes duplicates, leaving the most recent copy and removes lines matching several regexes.
[1]: https://github.com/naggie/dotfiles/blob/master/scripts/clean... [2]: https://github.com/naggie/dotfiles/blob/master/home/.bashrc#...
I also use it for doing interactive git rebases. I have an alias for doing a fuzzy rebase interactive (frbi):
frbi = !git rebase -i $(git log --pretty=oneline --color=always | fzf --ansi | cut -d ' ' -f1)^I made a few changes so I figured I'd post them back here in case you're interested:
pick-commit = # equivalent to your git log... cut...
rif = "!f() { rev=$(git pick-commit); [[ $rev ]] && git ri $rev~; }; f"
"rif" = "rebase interactive fzf/fuzzyfind". The function and test just makes it exit cleanly if you escape out of the filter instead of giving "fatal: invalid upstream '~'" (and I changed it from ^ to ~ because I think ~ is usually what one should use over ^, but I'm willing to be corrected). And obviously I already have 'ri' aliased to 'rebase -i'.While I'm here, here are my similar git aliases for adding, diffing, etc. files using fzf:
pick-status-files = "!f() { git status -z | xargs -0n1 | cut -c4- | fzi --print0 | xargs -0to git "$@"; }; f"
af = !git pick-status-files add
# and so on for diff, difftool, checkout, etc.
Oh, and 'fzi' (fuzzy-inline) is a small shell script that just calls: fzf --height 30% --reverse --multi "$@" rif = "!f() { rev=$(git pick-commit); [[ $rev ]] && git ri $rev~; }; f"
can just be: rif = !git pick-commit | xargs -tI% git ri %~Here's my git config with a bajillion aliases: https://github.com/kbd/setup/blob/master/HOME/.config/git/co...
Since git commands are one of the most common things to type, I alias all short git aliases like so: https://github.com/kbd/setup/blob/fd1f826a9365895b7a3b8b5c58...
(link to particular commit so line numbers stay correct). That way my workflow looks like:
$ ga (fuzzy find files to add)
$ gcm "commit message"
etc. pick-status-files = "!f() { git status -z | xargs -0n1 | cut -c4- | fzi --print0 | xargs -0to git "$@"; }; f"
can just be: pick-status-files = !git status -z | xargs -0n1 | cut -c4- | fzi --print0 | xargs -0t git
I'd recently converted it from a standalone script so I just converted it verbatim into a function.Mix it with https://github.com/rupa/z or https://github.com/skywind3000/z.lua for fuzzysearch/cd
And you can do cool scripts, like:
- search your vim edited files with highlighted preview: https://github.com/BarbUk/dotfiles/blob/master/shell/complet...
- search and open your chrome history: https://github.com/BarbUk/dotfiles/blob/master/shell/complet...
I kept loosing my history consistently on my new installation because of this. I usually put my laptop to sleep, and pretty much never intentionally terminate my x-session except on kernel updates. But because its a cheapo laptop with a crappy battery, I would occasionally run out of battery when asleep or it just wont wake up.
Most people seem to be using the PROMPT_COMMAND hook for stuff like this -- are you using something else, or did I miss it?
https://github.com/oilshell/oil/issues/320
I will add PROMPT_COMMAND to Oil, but I'm curious if there is something else I should add to support end-user customization.
One thing that might be interesting is to support a hook for the ! syntax, so the sqlite DB could be searched. I think that would solve some of the problems you mention in the code's comments, and maybe the fzf issue. (Although that literally just occurred to me, I don't promise to do this :) )
I haven't looked at how fzf works exactly. But if you want to chat about it please chime in on Github :)
[1] Latest status: Success With the Interactive Shell http://www.oilshell.org/blog/2019/02/05.html . Oil runs a bunch of interactive programs like bash-completion, virtualenv, and git-prompt!
I think a shell with an embedded sqlite, which makes sqlite accessible to scripts as shell builtins, and in which command line history is stored in a sqlite database instead of a plain text file, would be pretty cool. You could store command history (and maybe even other stuff) in something like $HOME/.shell_db As well as just a list of lines, other useful information could be stored for each line, such as current directory, timestamp, username (maybe I su to root and am running commands as root but still going into my own account's history database), hostname (if $HOME is on NFS, putting aside the issue that databases, including sqlite, don't work well over NFS due to file locking issues), etc.
what a disgusting, perturbed mind! Shell history is probably the kind of data that fits better as a text file. It is naturally textual and it can never grow too long, even if you are typing commands continuously for your entire life. Please, keep the dirty hands of sql far, far away from the shell!
Consider a table with five columns:
* uid
* pwd
* cmd
* exit-code
* datetime
Now you can `SELECT pwd,cmd WHERE exit-code != 0` to see all failed-commands.
cat history.txt | awk '$4 != 0'Not to mention $4 is completely arbitrary and it's impossible to tell without prior knowledge which field it's referencing.
The original query `SELECT pwd, cmd WHERE exit-code != 0` is both descriptive and won't break with future updates.
https://github.com/oilshell/oil/wiki/Structured-Data-in-Oil
Note that R has some popular packages that allow SQL syntax over data frames.
So basically the history file could be a textual table. You could trivially import it into a database, or you can just grep it.
The fact that somebody managed to bolt it onto bash after the fact is interesting. Oil is all about having a better/sane shell programming language, so it should have better hooks that make such customization. But yes it doesn't need to be in the core.
* Generate a new history file for every session (https://github.com/stilist/dotfiles/blob/master/dot_sh/sessi...)
* A 'histgrep' script that searches across all the files and highlights matches (https://github.com/stilist/dotfiles/blob/master/dot_sh/bin/e...)
## Better bash history
# Avoid duplicates
export HISTCONTROL=ignoredups:erasedups
# When the shell exits, append to the history file instead of overwriting it
shopt -s histappend
# After each command, append to the history file and reread it
export PROMPT_COMMAND="${PROMPT_COMMAND:+$PROMPT_COMMAND$'\n'}history -a; history -c; history -r"You can get decent history in ZSH just by setting the right options. If you want great history, you'll need to write a bit of code. Right now my history works as follows.
History is written to disk after every command. Up and Down keys go through the local history (from the same session). Ctrl+Up and Ctrl+Down go through the shared history (from all sessions). Ctrl+R also uses shared history (it's easy to add another binding for local history but I don't have it). Pressing Up/Ctrl+Up after typing something will go over history entries that have the matching prefix. For example, `git<Up>` will show the last command from the current session that starts with `git`. Here's my config: https://old.reddit.com/r/zsh/comments/bsa224/how_to_setup_a_....
In addition, my history is stored in Git. History from the local machine comes first, and from other machines second. This is fairly easy to do it Bash, too.
Using PROMPT_COMMAND to log every command in a separate timestamped log file. This allows you to use a much better format than bash history has to be in.
Easily readable to see what you've been doing and extremly efficient to grep for.
Has been discussed on HN before: https://news.ycombinator.com/item?id=11806553
export HISTFILE="${HOME}/.bash_history.$(hostname)"
shopt -s histappend is the default already I believe.
shopt -s cmdhist is also the default now.
HISTSIZE=-1 will be unlimited, which is fine for today's computers.I have yet to find a better way to store and search bash history than:
More recent bash sqlite history attempts all seem to fall short than this implementation, they store less metrics or have other caveats.
HISTFILESIZE and HISTSIZE can be set to -1 for unlimited.
Read man bash.
I would also like to know which distro clobbers user settable env variables.