24

I need to delete all commands in my history matching a string. I've tried:

$ history | grep searchstring | cut -d" " -f2 | history -d
-bash: history: -d: option requires an argument

$ history | grep searchstring | cut -d" " -f2 | xargs history -d
xargs: history: No such file or directory

$ temparg() { while read i; do "$@" "$i"; done }
$ history | grep searchstring | cut -d" " -f2 | temparg history -d
(no error, but nothing is deleted)

What is the right way to do this?

linuxfix
  • 381
  • 2
    While there are working answers below, I still wonder why it doesn't work with history -d X. I came across this question because I had just done history | grep search_str | sort -nr | awk '{print $1}' | while read i; do history -d $i; done. No error, but nothing deleted. Anybody can explain why? – mivk Jun 04 '13 at 17:47
  • 2
    See this question for the why https://unix.stackexchange.com/questions/599324/why-do-i-get-an-error-when-deleting-entries-from-bash-history – opticyclic Jul 19 '20 at 20:34

7 Answers7

30

The history command just operates on your history file, $HISTFILE (typically ~/.history or ~/.bash_history). It'll be much easier if you just remove the lines from that file, which can be done many ways. grep is one way, but you have to be careful not to overwrite the file while still reading it:

$ grep -v searchstring "$HISTFILE" > /tmp/history
$ mv /tmp/history "$HISTFILE"

Another way is with sed:

$ sed -i '/searchstring/d' "$HISTFILE"
Michael Mrozek
  • 93,103
  • 40
  • 240
  • 233
  • Good answer; $HISTFILE could contain spaces, probably worth quoting it. – Chris Down Dec 07 '12 at 20:50
  • 1
    Careful... histfile is unpredictable, if you have many open shells, it's no telling what exactly will get saved and in what order. Can anyone confirm that already open shells will not rewrite the history they loaded at initialization, when closed? – orion Jan 13 '15 at 17:02
  • 1
    "The history command just operates on your history file," -- I'm pretty sure history operates on the shell's runtime concept of history, which might or might not be saved to $HISTFILE at some point. The history mechanism works well without $HISTFILE at all, and AFAIK Bash doesn't even try to keep the history file consistent very aggressively. IIRC it only saves it on exit. – ilkkachu Jul 19 '20 at 22:32
12

Michael Mrozek's answer will work if you don't care about removing commands from the current session. If you do, you should write to the history file before doing the operations in his post by doing history -a.

Also, after you have removed the entries that you want from your history file, you can reload it by issuing history -c, then history -r.

Chris Down
  • 125,559
  • 25
  • 270
  • 266
9

For those looking for a one-liner:

while history -d $(history | grep 'SEARCH_STRING_GOES_HERE'| head -n 1 | awk {'print $1'}) ; do :; history -w; done

So, if you wanted to eg. delete multiple lines with a password in it, simply replace "SEARCH_STRING_GOES_HERE" with the password. This will search your entire history for that search string and delete it.

2 things to be aware of

  • grep uses regex unless you supply -F as an argument
  • The command will show 1 error, once there are no more matches. Ignore it.
thelogix
  • 262
  • I got an error (-bash: history: -d: option requires an argument), but it still worked? – paradroid Jan 15 '19 at 16:13
  • 2
    It runs the delete command continuously, until it fails with an error (because there's is nothing more to delete). Just ignore the error. – thelogix Jan 17 '19 at 14:53
  • 1
    This is not only onliner, but also the right simple answer. Yep don't be confused about the history: -d: option requires an argument error! It's at the END of the while loop => expected - it works and then basically says "I can't find anything else!" :) – jave.web Feb 29 '20 at 12:39
2

Based on Michael and Chris' answers, I came up with the following. Add it to your ~/.bashrc file and then load it with either . ~/.bashrc or source ~/.bashrc.

:<<COMMENT
    Deletes all lines from the history that match a search string, with a
    prompt. The history file is then reloaded into memory.
Examples
    hxf &quot;rm -rf&quot;
    hxf ^source

See:
- https://unix.stackexchange.com/questions/57924/how-to-delete-commands-in-history-matching-a-given-string

COMMENT #The unalias prevents odd errors when calling". ~/.bashrc" (May result in #"not found" errors. That's okay). unalias hxf hxf() { read -r -p "About to delete all items from history that match &quot;$1&quot;. Are you sure? [y/N] " response response=${response,,} # tolower if [[ $response =~ ^(yes|y)$ ]] then #Delete all matched items from the file, and duplicate it to a temp #location. echo -e "grep -v &quot;$1&quot; &quot;$HISTFILE&quot; > /tmp/history" grep -v "$1" "$HISTFILE" > /tmp/history

    #Clear all items in the current sessions history (in memory). This
    #empties out $HISTFILE.
    echo &quot;history -c&quot;
    history -c

    #Overwrite the actual history file with the temp one.
    echo -e &quot;mv /tmp/history \&quot;$HISTFILE\&quot;&quot;
    mv /tmp/history &quot;$HISTFILE&quot;

    #Now reload it.
    echo -e &quot;history -r \&quot;$HISTFILE\&quot;&quot;
    history -r &quot;$HISTFILE&quot;     #Alternative: exec bash
else
    echo &quot;Cancelled.&quot;
fi

}

Stephen Kitt
  • 434,908
1
cat "$HISTFILE" | grep -v "commandToDelete" >> "$HISTFILE" && exit

This worked for me. history -c && history -a didn't reload the history file properly for me. So instead I just exit my session right after rewriting the history file so the one in memory doesn't overwrite it.

Hydde87
  • 119
0

The following commands remove any history rows with ThisIs\*#$(#ATest123

for i in $(history | grep 'ThisIs\*#$(#ATest123' | awk '{print$1}' | sort -nr); do history -d $i;done
  • For loop through the command around $()
  • Show history
  • filter (grep) out only rows with ThisIs\*#$(#ATest123
  • Use awk to only show the first column (the numbers)
  • Reverse order of the numbers and sort in reverse r. Make sure to sort as a number n
  • Use the list of numbers from the previous command to delete each entry history -d $i

If working with a variable do the following

remove_password='ThisIs\*#$(#ATest123'
for i in $(history | grep '"$remove_password"' | awk '{print$1}' | sort -nr); do history -d $i;done
Dave
  • 601
  • 11
  • 23
0

Based on the idea from How can I remove duplicates in my .bash_history, preserving order?, I have following in may .bashrc,

cleanHistory() {
    cleanHistoryPattern="^Sp .+|^mf .+"
    history -w
    history -c
    # remove commands match pattern, still can use them in current session
    cat $HISTFILE | grep -v -P "$cleanHistoryPattern"|sponge $HISTFILE
    # remove duplications
    tac $HISTFILE | awk '!x[$0]++' | tac | sponge $HISTFILE
history -r

}

trap cleanHistory EXIT