oils-for-unix / oils

Oils is our upgrade path from bash to a better language and runtime. It's also for Python and JavaScript users who avoid shell!
http://www.oilshell.org/
Other
2.81k stars 154 forks source link

history should be customizable by the end user (HN thread summary) #320

Open andychu opened 5 years ago

andychu commented 5 years ago

This is a summary of Better Bash History discussion on HN

@emdash There is lots of interesting feedback here about history, including someone putting their history in an sqlite DB! Bash is crufty yet it's pretty amazing what people manage to hack on to it!

Some takeaways:

Things people are doing:

. .bash_cd_hook.sh
export PROMPT_COMMAND="history -a; check_cd; ${PROMPT_COMMAND:-:}"

History:

Other interesting quotes, not about history:

emdash commented 5 years ago

Good resources, I will look at FZF. I had not heard of it.

andychu commented 5 years ago

OK thanks!

The thread helped clarify what I'd want in Oil for history:

https://news.ycombinator.com/item?id=20057323

Basically it should be a structured text file. Coincidentally I wrote this the other day because @drwilly is working on xargs, which also needs structured data:

https://github.com/oilshell/oil/wiki/Structured-Data-in-Oil

The point is that you probably want history in SQL for two reasons:

I think what'ss weird about bash history is this:

That's why it's so weird between "sessions"! At least I think so.

This thread also points to the benefit of having a real programming language in shell. Instead of a PROMPT_COMMAND string to evaluate, we could have a hook:

func whenCommandFinished(command_string, evaluated_argv, timestamp, status, elapsed_time, ...) {
  # do something yourself with history, like put it in a sqlite database, or upload it to your own server, etc.
}
emdash commented 5 years ago

Pretty much agree.

The other thing I would add is making history more robust in the presence of events which kill the shell before it has a chance to save its history.

Either a function hook or a trap.

At least one comment hints at how controversial this sort of thing will be :P

On Fri, May 31, 2019, 10:03 andychu notifications@github.com wrote:

OK thanks!

The thread helped clarify what I'd want in Oil for history:

https://news.ycombinator.com/item?id=20057323

And coincidentally I wrote this the other day because @drwilly https://github.com/drwilly is working on xargs, which also needs structured data:

https://github.com/oilshell/oil/wiki/Structured-Data-in-Oil

Anyway, the point is that you probably want history in SQL for two reasons:

  • because you want fields without parsing them (usually done incorrectly). That can be done with the simple TSV2 proposal. Oil will parse that so you don't have to, but you can also grep the file if you want.
  • for concurrency. I don't think this is a problem for append only files (although admittedly I wonder how HISTCONTROL=erasedups is implemented).

I think what is weird about bash is this:

  • history is kept in a buffer in memory
  • on shell's exit, the whole thing is appended to a file.

That's why it's so weird between sessions! At least I think so.

This thread also points to the benefit of having a real programming language in shell. Instead of a PROMPT_COMMAND string to evaluate, we could have a hook:

func whenCommandFinished(command_string, evaluated_argv, timestamp, status, elapsed_time, ...) {

do something yourself with history, like put it in a sqlite database, or upload it to your own server, etc.

}

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/oilshell/oil/issues/320?email_source=notifications&email_token=AAAC3W47UY3GL4SQM7P7LILPYFK4XA5CNFSM4HRPAND2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODWVZZLA#issuecomment-497786028, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAC3WYTRSOL56T32ZVLJTLPYFK4XANCNFSM4HRPANDQ .

curusarn commented 4 years ago

I'm designing a history extension/replacement for bash and zsh and instead of having awkward two-level history I have a daemon-client type situation. Maybe oilshell could do something similar.

andychu commented 4 years ago

@curusarn I'd be interested in seeing what you've done for bash and zsh!

It would be interesting for Oil to support some kind of external protocol so people can plug in their own logic.

Although one limitation is that we use readline for history, which has its own file format. But bash has that issue too, so I'm curious how you solved it.

curusarn commented 4 years ago

@andychu Sorry for the long silence.

Readline is pretty difficult to work with.

The idea behind my project is to provide context-based history enhancement for zsh and bash. So I have a daemon that records the history with metadata and then handles all the interaction with history.

There are multiple ways I need to integrate with the shell:

To collect the history I run custom functions before/after each command using bash-preexec. It uses $PROMPT_COMMAND and DEBUG trap. It works with some minor issues.

Executing custom commands through readline bindings is problematic. There is bind -x builtin in bash to bind commands to key sequences but readline redraws the whole prompt when the command executes which is pretty slow. I asked in a readline mailing list about it but they told me it's not an easy thing to change at all (unsurprisingly). The redraw itself is so slow I have disabled arrow key bindings in bash in my project for now.

I didn't implement the binding for Ctrl+R yet but I want it to open an app similar to hstr except my app will use all the extra data for searching. There should be no problem to do this since hstr already works in bash.

All of the above is much easier to do in zsh then in bash.
Integrating with bash and readline is always a painful experience riddled with weird behavior and missing features. Integrating with zsh and ZLE (zsh line editor) is fairly straight forward.

Link to my project for more context and more info.

I'm not sure exactly how much of the difficulties I encountered were caused by readline itself and how much I can blame bash. My intuition is that the biggest problem are the bindings because anything else than native readline history functions takes the performance hit when redrawing.

abathur commented 4 years ago

I've also been picking at a bash-specific history module (opinionated; uses sqlite, also stays compatible with the underlying history functionality). I'm not promoting it for others to use, yet, but it's been in my shell for close to a year now, I guess. Don't have time for a longer comment atm, but I'll get notifications, and you'll know I have opinions. :)

andychu commented 4 years ago

@abathur What interface are you using to glue bash together with sqlite? Is it $PROMPT_COMMAND ?

That seems like a fragile interface but I suppose it's already there and a lot of people are using it.

I'm more interested in providing a good interface for Oil than developing this myself. Though it already implements $PROMPT_COMMAND ... maybe that's good enough but I'd be surprised if we couldn't do better.

abathur commented 4 years ago

Yep.

Indirectly, though--I've abstracted most messy implementation parts into a shell library (to keep them away from the history module itself, minimize duplicated work in other parts of my profile, and make it possible to share the PROMPT_COMMAND).

I do think Oil can do better, though I'm not sure it's incompatible with supporting PROMPT_COMMAND. Can probably discuss on zulip sometime soon.

curusarn commented 4 years ago

Oil could support preexec and precmd hooks that are supported by zsh. These could be easily used to plug in arbitrary history extensions to oil shell.

precmd Executed before each prompt. Note that precommand functions are not re-executed simply because the command line is redrawn, as happens, for example, when a notification about an exiting job is displayed.

preexec Executed just after a command has been read and is about to be executed. If the history mechanism is active (regardless of whether the line was discarded from the history buffer), the string that the user typed is passed as the first argument, otherwise it is an empty string. The actual command that will be executed (including expanded aliases) is passed in two different forms: the second argument is a single-line, size-limited version of the command (with things like function bodies elided); the third argument contains the full text that is being executed.

andychu commented 4 years ago

Yeah I think those hooks are a good idea, and the different versions of the command are what I wanted to sort out:

pre- and post- alias expansion is another possibility, and probably not hard, although I'm not sure how useful it is. (Almost all aliases can simply be shell functions.)

This distinction appears to be missing from bash's history mechanism on the "output side".

I would also favor something like prompt_hook() { } over $PROMPT_COMMAND.

Just like bash has command_not_found() { } now.

This also relates to $PS1 and $PS4. Basically the string hooks are annoying and error prone IMO.

andychu commented 4 years ago

related:

andychu commented 3 years ago

https://news.ycombinator.com/item?id=25946055

Good comment here

My histories are always saved because each shell instance gets its own HISTFILE, like so:

  export HISTFILE=$HOME/.history/${TTY##\*/}.$SHLVL

As I use different terminal windows for different tasks, this keeps history files rather concise thematically.

And I let the shell add timestamps too, so I can grep for entries produced during a certain time span:

zsh:

  setopt EXTENDEDHISTORY # add timestamps

bash:

  HISTTIMEFORMAT="%F %T "