10

I started using Mac recently and before I used to work on Ubuntu.

Suppose I run these commands one by one on my terminal:

python3 main.py
python3 main2.py
python3 main2.py
python3 main2.py
python3 main2.py
python3 main2.py
python3 main2.py

Now suppose I want to run python main.py again, so I will click the up key. I will need to click up key only twice on Ubuntu but 7 times on Mac.

If two consecutive commands are same, then terminal should remember only one command instead of remembering two different commands.

How can I make this happen on macOS?

1
  • 6
    Just to pedantic, it's not actually mac terminal that is doing this. It's bash, your shell running in the terminal. There are many shells and it's possible and common to replace the default shell with something else like zsh and even further to customize zsh with various scripts. – gman Nov 19 '17 at 18:35
13

You need to add the HISTCONTROL environment variable to your .bash_profile. In your .bash_profile add the following line:

export HISTCONTROL=ignoreboth:erasedups

Close and restart your bash session and the dupes should be gone. Alternatively, you could just execute that same line and it will take effect for that session (use it to test it out).


From the manual page for bash(in Terminal):

HISTCONTROL
A colon-separated list of values controlling how commands are saved on the history list. If the list of values includes ignorespace, lines which begin with a space character are not saved in the history list. A value of ignoredups causes lines matching the previous history entry to not be saved. A value of ignoreboth is shorthand for ignorespace and ignoredups. A value of erasedups causes all previous lines matching the current line to be removed from the history list before that line is saved. Any value not in the above list is ignored. If HISTCONTROL is unset, or does not include a valid value, all lines read by the shell parser are saved on the history list, subject to the value of HISTIGNORE. The second and subsequent lines of a multi-line compound command are not tested, and are added to the history regardless of the value of HISTCONTROL.

4

Why does this happen?

MacOS and Ubuntu are configured differently out of the box to handle duplicates in bash's command history. These configurations are stored in a number of so-called "dot-files". These take the form of ~/.bash* as well the system wide /etc/profile. All of these files might be customised to your liking and differentiate between interactive shells, login shells, remote shells etc. These files are read in a specific order and serve specific functions.

How to get the same behaviour on macOS?

If you want just this one, single customisation of "ignoring exact duplicates of command lines" you may go with something like Allan's answer, i.e. add a single single line to for example your bash_profile file. There is no "the proper way" but countless options.

In case this is not the only customisation for your bash then this might be not the best option:

A few other notes:

  • Anything that should be available to graphical applications OR to sh (or bash invoked as sh) MUST be in ~/.profile
  • ~/.bashrc must not output anything
  • Anything that should be available only to login shells should go in ~/.profile
  • Ensure that ~/.bash_login does not exist.

That means, when things get more complex it is good idea to spread out the customisations into multiple files, each of them specialised and highly ordered in their contents:

All exports may reside in their own file for simplified oversight.

Create a file that is read by bash at the root of your user directory, for example called .exports that contains:

# Omit duplicates and commands that begin with a space from history.
export HISTCONTROL='ignoreboth'; 

This needs to be "sourced" so that the file is read by bash on interactive startup:

Sourcing files
If you have a lot of shell configurations, you may want to split them out into several subfiles and use the source builtin to load them from your .bashrc: with adding source ~/.exports to it.

Alternatively, to ensure the files actually exist before loading

if [ -f ~/.exports ]; then
. ~/.exports
fi

The command . ~/.exports will source ~/.exports in the context of the currently running shell.

This is particularly useful for adding aliases, the separate file makes it easier to re-load them when you make changes.

4
  • 2
    How does .exports get read (sourced)? – fd0 Nov 19 '17 at 13:53
  • 1
    @fd0, By itself, it doesn't get sourced in macOS. Allan's answer is a proper way to achieve this. – user3439894 Nov 19 '17 at 14:10
  • If the value of HISTCONTROL is set in .bashrc and ~/.bashrc is sourced in ~/.bash_profile then there is no need to export the HISTCONTROL variable. Both an interactive login shell and an interactive shell will have the variable set. – fd0 Nov 21 '17 at 16:16
  • @fd0 Exactly: if. / In case I do not understand: My point was to provide an alt to Allan's simultaneously posted A / & keeping esp _profile but also the rest as clean and tidy as possible because this is for a scenario where lots of options/settings are considered. Is your last point a suggestion to add or meant as a correction as you may perceive the above method as 'wrong'? Now, I read your comment as between mine and Allan's in level of complexity addressed? – LаngLаngС Nov 21 '17 at 16:34
1

To uniqely record every new command is tricky. First you need to add to ~/.profile or similar:

HISTCONTROL=erasedups
PROMPT_COMMAND='history -w'

Then you need to add to ~/.bash_logout:

history -a
history -w

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .