That said, some form of advanced history search is a game changer, no matter how you get it. It's one of those "can't understand how I lived without it" things once you get it going.
If the command I want to match against is within the last few days or so, the fuzzy match performs ok, but anything from months ago which I know for a fact is in the history, it just doesn’t match it and instead shows irrelevant items (which technically I’m sure according to some algorithm are correct). Another one is I often have to put a ^ to ensure the ‘curl’ I’m searching for starts looking for matches at beginning of line.
Often times I still fall back to using fzf on ~/.zsh_history which seems to get what I want. There is this thread which is same issues I’m seeing, and I did try smart_sort=True but it’s still not great.
https://forum.atuin.sh/t/understanding-atuin-history-search/...
{ pkgs, ... }:
{
users.defaultUserShell = pkgs.zsh;
environment.systemPackages = with pkgs; [
atuin # ^R
eza # ls
git
zsh
];
programs.direnv.enable = true;
programs.nix-direnv.enable = true;
programs.zsh = {
enable = true;
enableCompletion = true;
enableBashCompletion = true;
enableGlobalCompInit = true;
# https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/docs/highlighters.md
syntaxHighlighting.enable = true;
syntaxHighlighting.highlighters = [ "main" "brackets" ];
# See `man zshoptions` for more details.
setOptions = [
# Remove duplicates continuously from command history (preserve newest entry).
"HIST_IGNORE_DUPS"
# Instantly share command history between all active shells.
"SHARE_HISTORY" # Alternative to: "APPEND_HISTORY", "INC_APPEND_HISTORY",
# Disable ^S and ^Z for less accidental freezing.
"FLOW_CONTROL"
# Save timestamp and duration of each command in command history.
"EXTENDED_HISTORY"
];
shellAliases = {
# Navigation
rm = "rm -iv";
ls = "eza -lg";
tree = "eza -lgT";
# git aliases
# ...
};
promptInit = ''
autoload -U promptinit
promptinit
prompt off
# Simple:
# PS1='[%n@%m:%~] %(!.#.$) '
# Colorful:
# PS1='[%F{green}%n@%m%f:%F{blue}%~%f] %(!.#.$) '
# Colorful with git branch:
function git_branch_name() {
branch=$(git symbolic-ref HEAD --short 2>/dev/null)
if [ ! -z "$branch" ]; then
echo -n " [%F{red}$branch%f]"
fi
}
# Omit username, print hostname + '$' with red when root, otherwise green:
prompt='[%(!.%F{red}.%F{green})%m%f:%F{blue}%~%f]$(git_branch_name) %(!.%F{red}#%f.$) '
# See: https://zsh.sourceforge.io/Doc/Release/Options.html#Prompting
setopt prompt_cr
setopt prompt_sp
setopt prompt_subst
export PROMPT_EOL_MARK=""
'';
interactiveShellInit = ''
# Prevent user-level "config missing" message.
touch $HOME/.zshrc
# MacOS
bindkey '^[[7~' beginning-of-line
bindkey '^[[8~' end-of-line
# Linux
bindkey '^[[1;3D' beginning-of-line # alt+left
bindkey '^[[1;3C' end-of-line # alt+right
bindkey '^[[1;5D' backward-word # ctrl+left
bindkey '^[[1;5C' forward-word # ctrl+right
# Both
bindkey '^R' history-incremental-search-backward
bindkey '^U' kill-whole-line
bindkey '^Y' yank
# My SSH endpoints don't recognize modern terminals
export TERM=xterm-256color
# ^R only
eval "$(atuin init zsh --disable-up-arrow)"
'';
};
}
programs.atuin = {
enable = true;
enableZshIntegration = true;
};
The chances of people reading this discussion running NixOS are small, and of those I'm sure the ones who were interested could search the internet for examples.
I just checked on their GitHub and it says "Additionally, it provides optional and fully encrypted synchronisation of your history between machines, via an Atuin server."
So you trust all of your shell commands to be stored on a server that you don't control?
Maybe I'm missing something here.
*: nothing syncs if you don't register, and you can even compile a version with the sync code flagged out if you REALLY don't want to trust anything
I prefer these two, you get good performance, search that is semi-shell syntax aware, ranking that takes age somewhat into account, and syntax hilighting.
https://github.com/zdharma-continuum/history-search-multi-wo... with https://github.com/zdharma-continuum/fast-syntax-highlightin...
or the same thing but older, for those who like older things because they have an air of stability about them: https://github.com/zsh-users/zsh-history-substring-search
There's nothing more annoying than having two fuzzy search implementations that behave mostly the same except in this or that case which subtly breaks your implicit mental model of it.
Consistency is underrated.
> McFly replaces your default ctrl-r shell history search with an intelligent search engine that takes into account your working directory and the context of recently executed commands. McFly's suggestions are prioritized in real time with a small neural network.
I know it's open source and people working on it might be reading this so let me apologize to you personally and please don't take this message as discouragement. I know how demotivating one bad review can be. It just wasn't working for me.
the reason bash uses Ctrl-r for searching back in history is because that's EMACS for searching back in your edit buffer (like C-a, C-f, C-b, C-e, etc are emacs cursor motions). I like the features of fzf, but I hate that it suddenly changes muscle memory that I rely on that is bigger than the immediate context.
these kinds of problems show up all over. I have always relied heavily on "middle button pastes the current mouse selection". I don't know what's going on in linux--is it Wayland?--but that feature is increasingly and increasingly being broken in more and more places. one place (which may entail a different "feature creep") is "click the url-bar in the browser and it auto-selects the whole thing...uh uh uh, wait, but not anymore for the purposes of the X-selection. but you can get the X-selection back if you click again (to deselect the whole thing) then again to reselect the whole thing... or is it double click..." the mind boggles
the old way (or goal) was to have the UI be standard, everywhere used the same code. but there were always people who wanted to innovate on their own, then with the idea of UX it wasn't any longer about standardization--or ironically personalization--at all
I realize whoever are making these changes simply don't work the way I have (for the last 35 yrs (well, that's just with linux)) so they don't notice what's being thrown away. I guess it's getting close to that time I should climb on and drift away on a little ice raft...
In my opinion, the one thing that would make a difference is a longer and broader history.
export HISTSIZE=-1; export HISTFILESIZE=-1
Also, one can bind the 'up' key to do the search action too, which I think is pretty convenient, my bindings are for zsh but I think it should also work in bash:
bindkey '^[[5~' up-line-or-history
bindkey '^[[6~' down-line-or-history
I honestly can't remember the entire setup, but my dotfiles that have the setup are here: https://github.com/Hobadee/dotfiles/blob/master/Common/profi...
The networking stuff can be fully compiled out of Atuin. The client and sync feature flags[1] control this, and it's fairly trivial to confirm that the networking crate (reqwest) is only enabled when the sync feature is enabled.[2]
Atuin can also be configured to only talk to your own sync server rather than the Atuin run server, and the history is e2e encrypted, so :shrug:. There's enough good engineering there that I'd not put it as particularly high on my list of security risks.
[1]: https://github.com/atuinsh/atuin/blob/e8df3d168e7fb9d6f99d97...
[2]: https://github.com/atuinsh/atuin/blob/e8df3d168e7fb9d6f99d97...
This. I've been using atuin for a few months and this is so horrible how much better it could be with a "real" fzf matching... I just tried skim shell integration ( https://github.com/skim-rs/skim/tree/master/shell ) and it's great. I kind of like the extra metadata atuin saves (cwd, session context), but I think I was just waiting to stumble into this to turn atuin back off...
I use fish shell, so you'll have to forgive any fishisms.
First, when you start atuin, don't bind to ctrl-r, instead manually bind it to something else. I use ctrl-t. This brings up the "standard" atuin interface which you can use to get the more detailed history - in particular the command inspector can be super helpful as is the ability to limit scope of history searches.
Next, bind ctrl-r to something like this: `commandline -r (atuin history list --print0 -f "{time} | {command}" | fzf --read0 --delimiter="|" --accept-nth 2 | sed 's/^ *//')`
In fish-speak, that's saying replace the command line with a command that fzf selects from your atuin history (which has been pretty printed to show the time of the command, but that won't end up on the command line).
Probably 95% of the time I'm using my new ctrl-r which searches atuin history using fzf. The other 5% of the time I'm looking for a command that I know I've ran in a particular directory, or using the atuin history to remove problematic entries from my history.
$ time atuin history list --print0 -f "{time} | {command}" > /dev/null
real 0m1.849s
(for some reason the built-in atuin search command doesn't take so long to show up? It might only fetch the last few entries from the db first... Eh, actually `atuin search` without argument which lists roughly the same thing run in less than half the time (0.85s), but -i is still another order of magnitude faster)Anyway, thanks - I'll fiddle with all this :)
Also the history is reloaded after each command so if I type multiple commands in a tmux pane x, and then go to another tmux pane y I just have to type something (just press the enter key) in pane y and I have the full history of what happened in pane x.
Here is how to do it, just add the following to your .bashrc for the eternal history :
```
export HISTFILESIZE=
export HISTSIZE=
export HISTTIMEFORMAT="[%F %T] "
export HISTFILE=~/.bash_eternal_history
export HISTCONTROL=ignoreboth
shopt -s histappend
shopt -s checkwinsize
filtered_history_save() {
local last_command=$(history 1 | awk '{print $4}')
# Don't store some commands in the history.
if [[ ! "$last_command" =~ (mpv|pass|yt-dlp|wtwitch) ]]; then
history -a
fi
history -c
history -r
}
export PROMPT_COMMAND="filtered_history_save; $PROMPT_COMMAND"
# Sources :
# http://stackoverflow.com/questions/9457233/unlimited-bash-history
# http://superuser.com/questions/575479/bash-history-truncated-to-500-lines-on-each-login
# http://superuser.com/questions/20900/bash-history-loss
```And for the custom fzf ctrl-r :
```
# Source fzf (should already be here if fzf is installed)
if [ -f /usr/share/fzf/completion.bash ]; then
. /usr/share/fzf/completion.bash
fi
if [ -f /usr/share/fzf/key-bindings.bash ]; then
. /usr/share/fzf/key-bindings.bash
fi
# Customize ctrl-r
export FZF_CTRL_R_OPTS="
--preview 'echo {2..} | bat --color=always -pl sh'
--preview-window right:wrap
--bind 'ctrl-/:toggle-preview'
--bind 'ctrl-t:track+clear-query'
--bind 'ctrl-y:execute-silent(echo -n {2..} | pbcopy)+abort'
--color header:italic
--header 'Press CTRL-Y to copy command into clipboard'"
```Also it made atuin useless to me since I didn't have a history anymore so couldn't even try it properly.
Not sure if that was the missing case for you, but just in case someone reads this and gets the same feeling as me
Well. I prefer ctrl-p personally, but I take your point:)
And the sudo !! pattern is something I do even when I realize that I need root ahead of time. There’s something about hitting enter on a command that makes me realize I’ve made a mistake, so doing that before I’ve granted root permissions is helpful. Up/ctrl-p are more awkward to use this way.
$ !:
& -- repeat substitution
A -- as ':a', then resolve symlinks
P -- realpath, resolve '..' physically
Q -- strip quotes
a -- absolute path, resolve '..' lexically
c -- PATH search for command
e -- leave only extension
g -- globally apply s or &
h -- head - strip trailing path element
l -- lower case all words
p -- print without executing
q -- quote to escape further substitutions
r -- root - strip suffix
s -- substitute string
t -- tail - strip directories
u -- upper case all words
x -- quote words, breaking on whitespace
It goes from the latest command down by default which isn't exactly the desired behavior but hesitated to add any other commands! I'm sure there is an option to reverse the order for a cli I'm already using but I couldn't find it in a cursory search :(
Feedback/commits appreciated.
Thanks
For anyone who's not familiar with something as basic as the history command, please pick up a book on bash/zsh/sh and take the time to learn.
If you don't know how to use the shell, you will struggle with (or simply be unable to perform) many basic tasks that could be accomplished quickly and easily by someone who's taken the time to learn how to use the shell and a handful of basic tools like grep, sed, awk, etc.
Shells are large programs. Everyone is unfamiliar with some bits because it just doesn't fit in their workflow.
Imagine how you'd rush to justify your lack of it here.
Everything you'd say in your defense would equally explain why someone wouldn't know the meaning of a certain unmarked number in the output of a random shell command.
I've seen myself transition. I've read python 2 stdlib docs cover to cover, and was better for it. It was also the last language for which I did that. I skimmed kotlin stdlib, and with go these days I hit up a search engine.
If everyone spent time going through every supposedly basic thing first, nobody would get anything done.
It's not at all obvious that the number is the history entry number, and I'm not even sure if knowing that brings any value to the average user.
> If everyone spent time going through every supposedly basic thing first, nobody would get anything done.
I’m not convinced that’d be so bad. We could all strive to do less. There’s too much bullshit “doing” in the world and not enough thoughtful pondering on what should be done.
If everyone spent the time to go through basic things, and thus had less time to do new things, then there would be fewer things to go through. And every new thing would be informed by what came before and not repeat its mistakes. And they would have had more thought put into it, thus be less buggy and dysfunctional, thus necessitating fewer alternatives.
It’s not going to happen, but everyone doing less and taking the time to learn the basics would likely make the world better.
I don't bother reading entire manuals either, but I know where they are. And if I'm using something often, I also try to learn how to best get help and information. Knowledge of the shell isn't mandatory, but can help greatly sometimes. If someone implement a feature to scratch an itch, it's nice to know about it when the same thing frustrates you too.
Some features just aren't worth the time.
> history | get 81201
╭─────────────────┬─────────────────────────────╮
│ start_timestamp │ 2025-03-26 05:16:58.184 UTC │
│ command │ echo hello world │
│ cwd │ /Users/matt │
│ duration │ 1ms │
│ exit_status │ 0 │
╰─────────────────┴─────────────────────────────╯
It's nice for confirming a suspicion that something is now slower than it used to be.Or even just the man page! bash(1)'s man page is a treasure trove, and it's a fairly short read (versus an entire book, at least).
man bash
I think shell history would be more practical if it appended to the history log after each command (rather than at the end of the session), and tagged each line with some form of "session ID"--allowing us to distinguish between "local" and "global" history.
You can force a flush to the log "as-is" with things like prompt overrides, but that's an all-or-nothing solution, saddling us with history from foreign shells even when we don't want it. Adding a session ID to the history log, and another flag to the built-in "history" command (to toggle between local and global history), would be a worthwhile improvement.
# disable flow control (Ctrl+S now searches forward in history)
if [ -t 0 ]; then
stty -ixon
fi
And then add the partial search to arrows: > cat .inputrc
$include /etc/inputrc
"\e[A": history-search-backward
"\e[B": history-search-forward
All that in bash.[1]: https://eli.thegreenplace.net/2013/06/11/keeping-persistent-...
Another important parameter is the number of successful runs: if you successfully ran the same command in the same directory 30 times in the last 30 days, it may be more useful that the one you have only run 1 time yesterday, so the exit value (like `false ;echo $?`) is another thing to keep
I have 2 bash shortcuts: one for a context-free search (Ctrl-T), while the default (Ctrl-R) is restricted to the current directory, and I throw everything (cwd, start time, stop time, exit value...) in a sqlite database to do more advanced queries (ex: on average, at which hour do I make more mistakes?)
Maybe skim does this? Or fzf does it if I configure it differently?
Ah, found this now, looks really promising! https://github.com/Aloxaf/fzf-tab (Oh, zsh specific)
Bacon replaces that command entirely for me (and many other cargo commands which would otherwise be run repetitively). https://dystroy.org/bacon/
make ./run_program
fzf could create a line for after I do the sam e sequence 3 times that goes:
make && ./run_program
That would be cool.
- where it was executed
- If it failed
- stickies/favourites
I've found bash's history-search-backward and history-search-forward very useful. They complete commands that begin with what you have typed. I have them mapped to M-p and M-n.
I also find myself using bash's insert-last-argument command, which can be repeated to fill in the last argument of previous commands. I have it mapped to M-_.
Another feature I've been using is the dabbrev-expand function, which xterm (and some other terminals) have. This allows string matching on the terminal output including the scroll buffer. It's useful for forming a new command that uses previous arguments that are not last arguments, or that are outputs of previous commands. I have it mapped to M-/. I've tweaked the matching algorithm on my local xterm to allow tokens enclosed in quotes or braces (which programs often output) to be matched (xterm by default only considers non-whitespace).
> fzf --zsh
unknown option: --zsh
> What does 5408 mean and why is it taking up valuable screen space?
It prefixes each line with a timestamp rather than a number, which makes most sense to me.
https://github.com/PatrickF1/fzf.fish?tab=readme-ov-file#-se...
It's popular though, so it's also available in zsh through the autosuggestions plugin.