Fixing macOS Zsh Terminal History Settings
blog.akatz.org
blog.akatz.org
preexec_custom_history() {
echo "$HOSTNAME $$ $(date "+%Y-%m-%dT%H:%M:%S%z") $1" >> "~/.fullhistory"
}
preexec_functions+=(preexec_custom_history)
I work on a lot of shared-filesystem computers, and so it's useful to be able to filter for commands ~year ago when I know what computer I was on when I ran it. I have a fzf binding to search through this with ctrl-r, and it's otherwise easily greppable.When I was still using bash, I had this same thing using https://github.com/rcaloras/bash-preexec .
How does the history know that the prefix (hostname, PID, date) is not part of the command though?
I mean, I tested it and it works as expected (without them) but I'm really curious about how it works.
There can be other reasons for wanting a smallish history besides fast startup like wanting low history numbers if you learnt all the ancient `!` history escapes that were the main way of using history with csh going back forty-plus years ago. This is also one of the reasons why extension theme frameworks like oh-my-zsh have become popular - much of what they do is enable bells and whistles that are features of the core shell.
HISTFILE The file to save the history in when an interactive shell exits. If unset, the history is not saved.
0. https://web.archive.org/web/20221012011928id_/zsh.sourceforg...
Saves me finding a command I ran but I don't remember when, or in which of my 50+ open and not committed shells. Also the exit code of every command is super helpful.
I suffered this frustration too, but adding this to my .bashrc solved it:
#write to history file at each shell prompt
export PROMPT_COMMAND="history -a; history -c; history -r; $PROMPT_COMMAND"
Every time a command is executed it's written to history right away. What you loose: the scroll-back buffer in each window pertaining to that session. So if you have a window running a set of commands, and you just 'up arrow' to re-run, you loose that because all history gets appended.I too would generally have 20+ open xterm windows but this gave instant command-to-history that I needed. fzf also helps instead of relying on up-arrow.
Thanks for sharing!
I am not sure what safe guards are needed before I can use this. Which makes it hard to recommend it. Especially since security is not mentioned at all on that page.
IIRC, putting a space in front of the command will prevent it from appearing in history...
1. iCloud sync is optional. You can sync the history, or notebooks (or both). Or keep everything always local only. 2. ShellHistory syncs with your private iCloud account, nobody other than person having access to your AppleID (with 2nd factor auth) cannot get access to it. 3. I would say it could be even better than storing your history in a file. Considering that with the ShellHistory you have access to "Full-Text-Search" and can really quickly find any accidental leaked information. 4. In v2 I have implemented an ignore pattern with RegEx, where you can define which commands you want to be ignored from saving to database. 5. I am on purpose build the app sandboxed and distribute it via App Store. It declares what it does. I don't and would never implement any custom telemetry collection, other than what App Store provides me already.
Although, I wonder if that might cause problems, if apps already link to SQLite and they might expect a different version, or if unstable apps might cause concurrency/corruption/etc issues with a shared per-user history DB. To avoid all that, maybe a per-user history daemon with a Unix domain socket?
sqlite-utils create-table ~/commands.db commands id integer text text --pk id
PROMPT_COMMAND="( fc -n -l -1 | perl -p -e 's/^\s+//; chomp if eof' | sqlite-utils insert --text ~/commands.db commands - & )"
It's slow, has perl & python external deps, needs a timestamp column, call subshell to avoid job control messages, ...A nicer single "prompt command" wrapper is certainly possible though.
setopt SHARE_HISTORY HIST_IGNORE_DUPS
SHARE_HISTORY will cause zsh to write to the history file after every command which means that two shells running in parallel won't override changes of each other and it will write a timestamp to the file too in order to have the history in chronological order even in light of multiple instances.
HIST_IGNORE_DUPS (or HIST_IGNORE_ALL_DUPS) will cause duplicated commands to not be written to the history file which helps with `Ctrl-R`ing
Looks like this may be what I need!
$ chsh -s /bin/bash
because I have 20 years worth of bash scripts that I've carefully tailored to work on bash in MacOS and Linux and I don't have time to rewrite them all just because Apple decided they don't like GPLv3. $ chsh -s /bin/zsh
You can’t please everybody.I'm also sad that pkgsrc seems to be abandoned wrt binary builds for macos - I could at least not find correct gpg keys for verifying the archives listed at: https://pkgsrc.joyent.com/install-on-macos/
Perhaps it's workable as a build-from-source package manager.
For now, it seems that macports have most of what I need - with less complexity than nix, and a better handling of upgrades than brew.
Does appear that brew moves faster, eg: https://trac.macports.org/ticket/65922 (i briefly tried a local build, but ran into dependency problems with libvterm that I didn't manage to resolve - there's a 0.3 [2] tarball, but the build system for neovim wasn't happy and kept finding 0.1.4 after it was uninstalled in favour of 0.3).
[1] https://nixos.org/manual/nix/stable/installation/installing-...
Also, I like zsh better.
export BASH_SILENCE_DEPRECATION_WARNING=1~
$ bash
The default interactive shell is now zsh. To update your account to use zsh, please run `chsh -s /bin/zsh`. For more details, please visit https://support.apple.com/kb/HT208050. bash-3.2$
~
So it's easy to switch between bash and zsh. It's quite rare for me to be in situation where I need to do something that only plays nice with bash, though.
See something I wrote a little while ago on this topic: https://jmmv.dev/2019/11/macos-bash-baggage.html
>
> However, if you issue 16 more unique commands, history | grep echo will no longer be able to find your 891 echo "test" entry.
On zsh, history only returns the last 16 commands, which is the default. But if you type `history 0`, it will return all commands since the zeroth index.
So if I want to search my entire history for instances of a command like this example, I don't modify anything, but just type `history 0 | grep echo` and that does what the author seems to be expecting.
So don't try to make zsh mimic bash, use it as-is and whenever something bothers you, try to understand both how the behaviour can be changed and consider the actual merit of the options.
But still, you can do better. I'm using a snippet from this page: https://spin.atomicobject.com/2016/05/28/log-bash-history/
export PROMPT_COMMAND='if [ "$(id -u)" -ne 0 ]; then echo "$(date "+%Y-%m-%d %H:%M:%S") $(pwd) $(HISTTIMEFORMAT= history 1)" >> ~/.logs/bash-history-$(date "+%Y-%m-%d").log; fi'
This give an easily greppable shell history. An entry looks like this:
2022-10-13 09:14:54 /home/bhaak 1000 vim ~/.bashrc
$ grep -i HIST /etc/zshrc
# Save command history
HISTFILE=${ZDOTDIR:-$HOME}/.zsh_history
HISTSIZE=2000
SAVEHIST=1000 >echo bollox
>history | grep bollox
and got a long line of results, dating back to 2019 [Who knew I swore so often in my terminal!]I don't have any of the fixes suggested in the article in my .zshrc so either it's duff info or not all OSX zsh installs are setup the same. Mind you, I'm still running OSX Mavericks so my zsh may not even have come bundled as the default shell. I may have enabled or installed it myself at a later date.
fc -l 1 | grep foo