How do I prevent commands from showing up in Bash history? - bash

Sometimes, when I run commands like rm -rf XYZ, I don't want this to be recorded in Bash history, because I might accidentally run the same command again by reverse-i-search. Is there a good way to prevent this from happening?

If you've set the HISTCONTROL environment variable to ignoreboth (which is usually set by default), commands with a leading space character will not be stored in the history (as well as duplicates).
For example:
$ HISTCONTROL=ignoreboth
$ echo test1
$ echo test2
$ history | tail -n2
1015 echo test1
1016 history | tail -n2
Here is what man bash says:
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.
See also:
Why is bash not storing commands that start with spaces? at unix SE
Why does bash have a HISTCONTROL=ignorespace option? at unix SE

In your .bashrc/.bash_profile/wherever you want, put export HISTIGNORE=' *'. Then just begin any command you want to ignore with one space.
$ ls # goes in history
$ ls # does not

Even better use HISTIGNORE. This allows you to specify a set of patterns to be ignored (such as rm). It is better (I think) than just piping all history to /dev/null.

kill -9 $$
I know that is not as best as the previous answers, but this will kill the current Bash shell without saving anything, useful when HISTCONTROL is not set by default, you forgot to set it, or pure and simple you forgot to put a leading space and you just typed in some passwords and don't want them to remain permanently in history.
This is the quick way, and something like erasing the history file is not as good because you need to do it outside a history saving shell (log in as different user and use su/sudo, creating a background job, etc.)

You can do one of two things:
export HISTFILE=/dev/null
Or, begin the command with a space.

Or
unset HISTFILE
(similar to the previous answer only shorter: export HISTFILE=/dev/null)

I added an "Incognito" functionality to my .bashrc for when I want to run some commands without being saved without having to add spaces before each one.
Do note though that the in-memory history of the current terminal session will still be saved, but when I open a new terminal the commands issued in a past terminal's incognito session will never be seen because they were never written to the HISTFILE.
To your .bashrc:
ignoreHistory="false"
DEFAULT_HISTFILE=~/.bash_history
HISTFILE="$DEFAULT_HISTFILE"
# Toggle incognito mode
incognito() {
if [[ "$ignoreHistory" == "true" ]]; then
echo -e "\e[33mExited incognito mode\e[39m"
ignoreHistory="false"
HISTFILE="$DEFAULT_HISTFILE"
else
echo -e "\e[33mEntered incognito mode\e[39m"
ignoreHistory="true"
HISTFILE=/dev/null
fi
}
Nice little utility I think some people may find use in, you can even change the prompt to reflect whether you're in incognito mode or not.

At shell startup, I explicitly cleanup the history from the entries that I don't want to be there. For example, I don't want any rm -rf in the history (it's trauma after removing a directory full of results processed overnight, just with a single Arrow-Up + Enter :)
I put the following snippet in my init file (works with .zshrc, should also work with .bashrc)
# ...
HISTFILE=~/.zshhistory
# ...
# remove dangerous entries from the shell history
temp_histfile="/tmp/$$.temp_histfile"
grep -v -P '^rm .*-rf' $HISTFILE > $temp_histfile
mv $temp_histfile $HISTFILE

Related

How to write a bash script or function that removes itself from history?

I would like to write a bash script or function that is able to remove the entry from the caller's bash history that corresponds to its own invocation. Is this possible?
The use case is a wrapper around writing secrets on a team-owned cluster, and I want to add some convenience around making sure all team members don't inadvertently leave the secrets in plain text in bash history. At first I was going to require the script use read -rs to prevent the command from containing the secret. Then, realizing that some will inevitably type in the secret name in the command invocation anyway, I wondered if I could support that but delete the invocation bash history.
Essentially, something like adding history -d $HISTCMD in the script; however, the history item isn't written until after the script completes. So no dice.
Is there a way to tell bash "do not record this command in history" or otherwise delete it upon completion?
Check out HISTIGNORE
A colon-separated list of patterns used to decide which command lines should be saved on the history list.
$ HISTIGNORE='echo *'
$ echo secret
secret
$ history 3
9921 man bash
9922 HISTIGNORE='echo *'
9923 history 3

How to delete a single command from history in bash

After some searching online I found that the following command should remove a single entry from the bash history:
$ history -d <number>
So let's say I would like to remove this single line from my history because I misspelled git
:
10003 gti add . && git commit -m
When I do the following command:
$ history -d 10003
It still shows up in my history, even when I restart the terminal
So any idea how I can fix this because I use autocomplete and sometimes especially when using git it can get a bit messy.
Command history in Bash is stored both in-memory, typically for the current shell session, and on disk, by default in ~/.bash_history. A default history configuration might append the in-memory session to the file on disk when the session is terminated, but there are a lot of knobs to tweak.
In any case, to permanently delete a command from history, you might have to edit the ~/.bash_history file directly.
A few references to history settings:
HISTFILE – the file where history is persisted (defaults to ~/.bash_history)
HISTFILESIZE – the number of commands stored in the history file (defaults to HISTSIZE)
HISTSIZE – the number of commands stored in the (in-memory) history list (defaults to 500)
cmdhist – shell option to save multi-line commands in a single history entry
histappend – shell option to control if the history list is appended to the history file, or if the history file is overwritten
lithist – shell option to store multi-line commands using linebreaks instead of semicolons
So turns out it's solved by deleting the command from ~/bash_history

How to view the output of history from a non-interactive shell given path to HISTFILE?

In emacs I would like to use shell-command-to-string to essentially retrieve something like history | tail -n5. Issue is that history is a bash built in (in interactive mode only?) so that does not work. As I am merely interested in a read-only snap of the decorated output, is there a way to parse the content of the history file (suppose it is ~/.bash_history) and output it with the line number and date as the history bash command does it?
In other words, is there a way to do something like:
(shell-command-to-string "history --pretty-dump-to-stdout-with-date-time-linenum --filename ~/.bash_history")
? (Or, perhaps, a different workaround?)
Make history command output something
I expect set -o history to fail as it will use the history file $HISTFILE which most likely is unset for non-interactive shells. Assuming the history is stored in ~/.bash_history you could execute ...
HISTFILE=~/.bash_history; set -o history; history | tail -n5
However, this would modify your history by adding history | tail -n5. A better approach is to read the history file without enabling the history:
history -r ~/.bash_history; history | tail -n5
Commands from your current session
Assume you are inside a interactive terminal session and execute ...
$ myCommand
$ emacs # and inside emacs the shell command `history` as shown above
... then you most likely won't see myCommand in the history. This is because bash only writes the history to your history file when closing the session. You can manually update the history file using history -a, thus the following should work:
$ myCommand
$ history -a
$ emacs # and inside emacs the shell command `history` as shown above
Since most people usually would forget the history -a it would be best to automate this step in your .bashrc, either at each prompt (PROMPT_COMMAND="history -a), or only for emacs (emacs() { history -a; command emacs "$#"; }).
Pretty Printing
is there a way to do something like [...] --pretty-dump-to-stdout-with-date-time-linenum
The history format of the history command is controlled by HISTTIMEFORMAT. The format has to be set and exported before calling history inside emacs.

Is it possible to search all bash histories

If you have 10 xterms open, say, is it possible to search all the bash histories combined in some way?
I can do
history|grep something
on each one separately but I would sometimes like to avoid that.
I am going to assume these xterm windows are all for the same userid, and are all on the same host. If they are not all the same user, then you must change their history file locations to use the same history file. If you need to do this, see this thread: https://superuser.com/questions/760830/how-do-i-change-the-bash-history-file-location
But the main problem you will have is that bash only writes its history to .bash_history when the shell is closed. However, you can edit your .bashrc to write the most recent command to .bash_history immeditately, like so:
# When the shell exits, append to the history file instead of overwriting i
shopt -s histappend
# After each command, append to the history file and reread it
export PROMPT_COMMAND="${PROMPT_COMMAND:+$PROMPT_COMMAND$'\n'}history -a; history -c; history -r"
See this link for more info: https://unix.stackexchange.com/questions/1288/preserve-bash-history-in-multiple-terminal-windows
Bash has in-memory history and a history file ($HISTFILE, ~/.bash_history by default). By default, all your bash sessions share one history file which gets by the in-memory history of the last exiting bash shell whenever a bash shell exits.
You can change the behavior from overwriting to appending with:
shopt -s histappend
And you can manually invoke this dumping with
history -a #-a stands for append
What I do is I have history -a as part of my PROMPT_COMMAND so that each new prompt line adds a new record in the global history file.
Then, whenever I need the history from other sessions, I just load the global history file into the in-memory history with:
history -r
-r read the history file and append the contents to the history
The manual of the history command can be read with help history and more details regarding history are in man 1 bash.
Apart from setting shopt -s histappend and adding history -a to your PROMPT_COMMAND, it's also useful to set sizes (HISTFILESIZE, HISTSIZE) to some large values and HISTCONTROL, e.g. to "ignoreboth:erasedups" to eliminate duplicates.
Also, I find Ctrl-R to be a much more comfortable way of searching through history.

How do I get "previous executed command" in a bash script?

I use multiple bash sessions, and I want to keep track of history from all of them in one file (I don't care that it is multiplexed from multiple sessions, I can always put a session identifier in front of it.) I have tried doing
shopt -s histappend
and also adding
history -a
to the $PROMPT_COMMAND variable. But none of them really work for me, and I don't understand why they don't work (they behave very non-deterministically as far as I can tell... sometimes they multiplex commands from multiple sessions, sometimes they don't).
The goal of this question is to explore an alternate way to keep history from all sessions, where I can control what I write to the history. The idea is to store "previous command" in a shell variable and then echo that variable to a history-log file inside the definition of PS1 variable.
The question is: How do I get the "previous executed command" in a shell variable. I know I can execute echo !! >> logfile.txt in interactive bash session to record it to a log file. But how do I do this in a script file (or .bashrc file)?
I have tried
PROMPT_COMMAND="PC=$_;"
PREVIOUS_COMMAND=$(echo $PC) # $_ only gives the last argument of previous command
export PS1="[\u#\h \w] [$PREVIOUS_COMMAND $(echo $_) $_] $ "
But none of this works.
Thanks for your time,
~yogi
Something like
fc -ln -1
should work. That said, you're probably running into concurrent access issues (read: multiple shells overwriting each others' history) and you may not be able to do any better by hand.

Resources