When accidentally pasting a file into the shell it puts a ton of ugly nonsense entries in the bash history. Is there a clean way to remove those entries? Obviously I could close the shell and edit the .bash_history
file manually but maybe there's some kind of API available to modify the history of the current shell?
14 Answers
Just this one liner in command prompt will help.
for i in {1..N}; do history -d START_NUM; done
Where START_NUM is starting position of entry in history. N is the number of entries you may want to delete.
ex: for i in {1..50}; do history -d 1030; done
-
4I wonder why this is not a built-in function already. 'history' is a very old tool. Aug 1, 2016 at 9:23
-
1serves the purpose very well, but this command can be seen in history :) Nov 26, 2018 at 4:12
-
1askubuntu.com/a/978276/22866 has a nice way to delete the "delete from history command" from history :-) Jul 4, 2019 at 6:46
-
2@RajeevAkotkar If the delete command is the nth line, then using N+1 in the for loop will also help delete the command that did the deleting. Nov 29, 2019 at 2:55
-
1Alternatively, you can also use
for i in {1030..1080}; do history -d 1030; done
instead - which might be a bit more intuitive. I wrote a small bash script, just replace1030
with$1
and1080
with$2
.– CadoizJan 6, 2022 at 14:39
As of bash-5.0-alpha
, the history
command now takes a range for the delete (-d
) option. See rastafile's answer.
For older versions, workaround below.
You can use history -d offset
builtin to delete a specific line from the current shell's history, or history -c
to clear the whole history.
It's not really practical if you want to remove a range of lines, since it only takes one offset as an argument, but you could wrap it in a function with a loop.
rmhist() {
start=$1
end=$2
count=$(( end - start ))
while [ $count -ge 0 ] ; do
history -d $start
((count--))
done
}
Call it with rmhist first_line_to_delete last_line_to_delete
. (Line numbers according to the output of history
.)
(Use history -w
to force a write to the history file.)
-
1Since the OP asked for deleting the N last lines, this script should be modified by doing something like:
tot_lines=$(history | wc -l)
and then repeathistory -d $(( tot_lines - $1 ))
. Jan 19, 2018 at 9:13 -
2Instead of
$(history | wc -l)
, there is the variable$HISTCMD
that can be used. Jan 19, 2018 at 9:58
The history
bash built-in command also takes a range, now in 2020:
-d offset
Delete the history entry at position offset. If offset is negative, it is interpreted as relative to one greater than the last history position, so negative indices count back from the end of the history, and an index of -1 refers to the current history -d command.
-d start-end
Delete the history entries between positions start and end, inclusive. Positive and negative values for start and end are interpreted as described above.
Only the command itself does not tell with --help
:
Options:
-c clear the history list by deleting all of the entries
-d offset delete the history entry at position OFFSET. Negative
offsets count back from the end of the history list
-a append history lines from this session to the history file
...
E.g. history -d 2031-2034
deletes four lines at once. You could use $HISTCMD
to delete from the newest N lines backwards.
You can also export with history -w tmpfile
, then edit that file, clear with history -c
and then read back with history -r tmpfile
. No write to .bash_history.
-
3
-
-
The range parameter is available in Bash 5. It's not available in version 4 Jun 7, 2022 at 3:53
If you delete line N
from the history, then line N+1
moves in position N
etc.
For this reason, I prefer identifying the oldest
and newest
history line between which I want to delete all history. (Note: oldest
< newest
).
If for instance I want to delete the history lines from oldest = 123
up to newest = 135
, I'd write:
$ for i in {135..123}; do history -d $i ; done
I find it easier to read; besides: the for
command can also decrement a range...
-
2Instead you could also just do
for i in {123..135}; do history -d 123; done
– CadoizDec 16, 2021 at 7:37
the history -d
arg takes a range and $HISTCMD
is the max number in the history.
This function works to remove the last n
entries from history (just pass in the number of history commands to remove like, eg rmhist 5
:
$ rmhist() { history -d $(($HISTCMD - $1))-$HISTCMD ;}
Or.. Go fancy with an arg like this to remove from a point in history (inclusive) or last 'n' commands:
rmhist() {
case $1 in
--from|from) local start=$2; ;;
--last|last) local start=$(($HISTCMD - $2)) ;;
*) echo "Try rmhist --from # or rmhist --last n "; return ;;
esac;
history -d ${start}-${HISTCMD}
}
The result looks something like this:
5778 ls /etc
5779 ls /tmp
5780 ls /dev
5781 ll
5782 cd /tmp
5783 cd
5784 history
(base) ~ $ rmhist --last 3
(base) ~ $ history 5
5778 ls /etc
5779 ls /tmp
5780 ls /dev
5781 ll
5782 history 5
(base) ~ $ rmhist --from 5780
(base) ~ $ history 5
5776 history 10
5777 ls
5778 ls /etc
5779 ls /tmp
5780 history 5
-
I get "bash: history: N: history position out of range" errors, but this worked for me to delete the last entry
hdl() { history -d $(($HISTCMD - $1))-$(($HISTCMD - 2)) ;}
I'll write any error understandings in my answer here: unix.stackexchange.com/a/573258/346155– alchemyMar 18, 2022 at 20:59
What's different in this answer: it also displays what is being deleted.
For those using Bash 4.x (e.g. in Centos 7 and lower), I use this to delete the last N items in the history including the command to do this. (here N=5)
for i in {1..5}; do
echo "clearing line $(($HISTCMD-2)): $(history -p \!$(($HISTCMD-2)))";
history -d $(($HISTCMD-2));
done;
history -d $(($HISTCMD-1))
To delete the history lines between n and m (e.g. here 544 and 541), I use the following. NOTE: you must enter the bigger line number first and then the smaller one:
for i in {544..541}; do
echo "clearing line $i: $(history -p \!$i)";
history -d $i;
done;
history -d $(($HISTCMD-1))
The answer by user2982704 almost worked for me but not quite. I had to make a small variation like this.
Assuming my history is is at 1000 and I want to delete the last 50 entries
start=1000
for i in {1..50}; do count=$((start-i)); history -d $count; done
It works for me. To delete line 2 to 29 (includes line 2 and line 29):
history -d 2-29
history -w
This should delete every history list entry in reverse order N times:
function hd { for i in $(seq 1 $1); do history -d $(($HISTCMD-1)); done; }
If you want to add this to your .bashrc, use:
echo "function hd { for i in $(seq 1 $1); do history -d \$((\$HISTCMD-1)); done; }">>~/.bashrc
..and then source ~/.bashrc
to reload bash config. Use as:
hd <N>, where N is the number of lines to delete
My other answer for a different approach that is not working, but is long and complicated.
Also see How to delete history of last 10 commands in shell?
EDIT: this started giving me the error "bash: history: -N: history position out of range" as does history -d -2
for some reason. So I used another users answer, that even though it gave me the same error, I could adjust to make work. This example deletes only the last line, but can be extended.
hdl() { history -d $(($HISTCMD - $1))-$(($HISTCMD - 2)) ;}
Before I give up on this alternative approach, I want to put down what I've learned. Part of my difficulty in debugging my first answer is that its a bit unclear how many N ends up being, because it includes the current command, so I think it is really N-1 or maybe N-2.
To make it more clear, I was trying a solution that would delete from a line number going forward. And wow, what a big surprise of how difficult that is. The main problem is the history routine is running in the background so deleting say number 50 will result in number 51 now being 50 in less than a second. So I tried making a function to delete going backwards from the end. And when trying to deleting 10 in a row and it gets about 5 before running out of commands as they shift down. So I tried making a function to delete forwards and it, for some reason only gets every other one. Consistent with the history routine running about half as fast as the function. I also tried inserting sleep at certain points.
I tried turning off history temporarily, but then $HISTCMD show the total from $HISTFILE not the history list. So it looks nary a possible. My goal was to clean the history of unnecessary commands so that I could memorize the bang number !# and use the same ones going forward for frequent and difficult commands.
Here's some of the functions I tried. Maybe someone can improve or diagnose:
Going from the Number to the end:
function hd2 { a=$HISTCMD; echo $a; echo $1; echo $(($a%$1)); for i in $(seq $1 $a); do echo $i; history -d $i; done; }
Deleting from the end repeatedly with sleep:
function hd3 { a=$HISTCMD; echo $a; echo $1; echo $(($a%$1)); for i in $(seq 1 $a); do history -d $HISTCMD; sleep 1; done; }
Deleting the Number repeatedly with sleep:
function hd4 { a=$HISTCMD; echo $a; echo $1; echo $(($a%$1)); for i in $(seq 1 $a); do history -d $1; sleep 1; done; }
The beginning echo variable are for debugging. % is remainder or mod. $HISTCMD does not carry through command substitution and need to be assigned as a variable.
new idea: since the function below works with repeated use:
echo "function hdn { history -d \$1; }">>~/.bashrc
echo "function hdn2 { for i in $(seq 1 \$2); history -d \$1; sleep 1; done; }">>~/.bashrc
This thread is old by has a reply from last year, but I came here looking for the same answer. Basically the simplest solution I found in the end was to simply delete one line at a time, which sounds counter intuitive but bare with me, a duplicate history -d 123
does not show up, if you type it 10 times (up arrow each time or paste, paste, paste), line 123 is deleted each time, but when you type "history" again, the line history -d 123
only shows once.
Eg:
$ history
122 ls
123 history
124 ls
125 sudo
126 su
127 host
128 history
129 history -d 123
130 ls
131 history
$ history -d 123
$ history -d 123
$ history -d 123
$ history -d 123
$ history -d 123
$ history -d 123
$ history -d 123
$ history -d 123
$ history -d 123
The output becomes:
$ history
122 ls
123 history -d 123
124 history
As you can see the history -d 123
is not duplicated for each additional time it is typed, it only shows once, so that is the easiest way to get rid of multiple lines without having to use scripts or other tools.
If the copy paste garbage is just a single password you can simply:
history -d -2
to delete just that one last line in your history.
It's -2
rather than -1
because -1
is the current command you are typing (history -d -1
). So with -1
you'd end up with an unchanged buffer and .bash_history
file.
The popular community answer above led me to this solution. And I was desperate for a command I could easily remember, without having to duckup this answer every time I uckup my history (on servers where I haven't aliased one of the other answers here).
Let's assume you want to delete the last 5 entries of your history as an example:
for i in {1..5}; do history -d $(($HISTCMD-1)); done
If you want to include the same deletion command to removal it is simpler:
for i in {0..5}; do history -d $HISTCMD; done
Note that ranges in for loops are only there to repeat N times the desired action.
-
This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker. - From Review Aug 29, 2022 at 9:29
-
(1) The question talks about N. I guess you are using “5” as an example value for N. If so, you should say so. (2)
for i in {0..5}; do
something
; done
doessomething
six times. But, if thesomething
doesn’t reference$i
, thenfor i in {1..6}; do
would produce the same result. So how is it possible thatfor i in {0..5}
would delete itself from history butfor i in {1..5}
wouldn’t? … … … … … … … … … … … … … … … … … … … … … … … … Please do not respond in comments; edit your answer to make it clearer and more complete. Sep 3, 2022 at 6:18
Tried with below command and it worked fine
end=`history| awk 'END{print $1}'`
start=`history| awk 'END{print $1-10}'`
awk -v end="$end" -v start="$start" '{for(i=start;i<=end;i++){print "history -d" " " i};exit}’|sh
-
1Will this remove entries from bash's history if you pipe it through
sh
??– Jeff Schaller ♦Mar 17, 2020 at 11:10