3

Does anyone know what's occurring with zsh below and how to get the same behavior I show with ksh and bash?

I'm noticing zsh on MacOS v13.7 is skipping even commands numbers in its history output, while ksh and bash behave as expected. zsh history count is also wrong, it is 6 vs 7 commands.

zsh session:

$ history 1 history $ ls ... $ history 2 ls $ ls df ls: df: No such file or directory $ history 2 ls 4 ls df $ ls / $ history 2 ls 4 ls df 6 ls / 

ksh and bash session:

$ ksh $ history 1 history $ ls $ history 1 history 2 ls 3 history $ ls df $ history 1 history 2 ls 3 history 4 ls df 5 history $ ls / $ history 1 history 2 ls 3 history 4 ls df 5 history 6 ls / 7 history 

CONFIGARTION FILES:

Keep .common-hist (relavant):

export HISTFILE="${HOME}/.${HDIRPRE}-history.d/history-"`uname -n`"-"`id -nu`"-"`tty|cut -c6-`"" 

.zshrc (relavant):

HDIRPRE="zsh" precmd() { setprompt } 

.kshrc (relavant):

HDIRPRE="ksh" function prompt_cd { command 'cd' "$@" && setprompt } alias cd="prompt_cd" 

.bashrc:

HDIRPRE="bash" PROMPT_COMMAND=setprompt 

I call the function setprompt to configure an xterm or Apple Terminal title bar and prompt by setting ${PS1}. I'm not sure if that's the 'extra' command missing from zsh history. Perhaps it's MacOS related.

1 Answer 1

3

You likely have the histignorealldups (aka HIST_IGNORE_ALL_DUPS, case and underscores are ignored in option names¹) option set, which deduplicates entries in the history. Check the output of setopt.

With that option set, when you enter a history command line, the previous occurrence of that command line in the history is removed. Those were commands 1, 3 and 5 in your example.

From info zsh histignorealldups:

HIST_IGNORE_ALL_DUPS
If a new command line being added to the history list duplicates an older one, the older command is removed from the list (even if it is not the previous event).

(see also histignoredups which only deduplicates consecutive history entries).

There are similar features in tcsh:

histdup (+)
Controls handling of duplicate entries in the history list.

If set to all only unique history events are entered in the history list.

If set to prev and the last history event is the same as the current command, then the current command is not entered in the history.

If set to erase and the same event is found in the history list, that old event gets erased and the current one gets inserted.

Note that the prev and all options renumber history events so there are no gaps.

and yash:

HISTRMDUP
This variable specifies the number of command history items to be checked for duplication. When the shell is adding a new history item to the command history, if some of the most recent n items have the same contents as the new one, then the duplicate existing items are removed from the history before the new one is added, where n is the value of this variable.

If the value of this variable is 1, for example, the most recent item is removed when a new item that have the same contents is added. Items older than the nth recent item are not removed. No items are removed if the value of this variable is 0. All items are subject to removal if the variable value is greater than or equal to the value of the HISTSIZE variable.

And AFAICT, fish does it by default, but AFAIK bash and the various implementations/variants of ksh have no equivalent.


¹ Which can make it difficult to find where that option is set in your config files. grep -ri 'hist_*ignore_*all_*dups' ~/.zsh* would likely work even if it wouldn't catch a set -o hi_stig_no_REAL_LD_UPs. Or you could use a findopt function defined as findopt() grep -ri -e${^argv///'_*'} ~/.zsh* to catch everything.

9
  • It feels a bit surprising it'd accept hi_stig_no_REAL_LD_UPs, even though it is the logical conclusion of ignoring all underscores. It feels wrong. I suppose one could preprocess the files with something like for f in ~/.zsh*; do printf "%s:\n" "$f"; < "$f" tr -d '_' | grep -i 'histignorealldups'; done Commented Jan 30 at 7:37
  • @ilkkachu, in yash, it's case, underscore and hyphens that are ignored. To me it feels less wrong than bash where some option names have underscores, some hyphens and some not without consistency (see also options like varredir_close or complete_fullquote with _s only between some of the words) and they're not optional (not to mention the fact that it has 2 sets of options for no good reason). See edit for a more foolproof way to find option definitions. Commented Jan 30 at 7:55
  • heh, oh no, I'm not saying Bash isn't insane in some ways, it is. But just because program A does something stupid, it doesn't mean program B can't also do something stupid. And regardless of people repeating Postel's law as some sort of dicta, being too lax in accepting different forms of the same thing does have downsides, one of which is what you showed here, that detecting and recognizing things becomes much harder. Commented Jan 30 at 11:25
  • What ever the other idiocies in Bash, at least the inconsistently named options there only have one name. While in zsh, the output of setopt and the documentation aren't even consistent with each other in the form they use. Commented Jan 30 at 11:25
  • 1
    @ilkkachu, I won't deny that you have a point. That lax approach was probably added to improve interoperability when you don't know where the next shell will put -s and _s or negate the meaning and use a no prefix. See also the many ways to set options in zsh (setopt/unsetopt (native), set -o/+o (ksh/POSIX interoperability), set -? (with csh and ksh/sh variants) options[opt]=on... since the introduction of zsh/parameters introspection variables). Commented Jan 30 at 11:50

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.