Saturday, February 16, 2008

how to delete last command from bash history

If you have ever typed something into a command prompt that you wished you hadn't - you may find it useful to know that you can delete it from ~/.bash_history very easily.

The command:
history -d offset
will delete the history entry at position offset.

# history
1 cd
2 history
3 ls -alhF
4 history
5 wget username:password@private.ftp.com/secret/file.tar.gz
6 history


so to delete the wget command (which contains a password) - just use:
history -d 5

# history -d 5
# history
1 cd
2 history
3 ls -alhF
4 history
5 history
6 history -d 5
7 history




This is (hopefully) the final update to an OLD post.  Sorta "pre-StackOverflow" internet.  

Checkout this question instead:
http://superuser.com/questions/384366/remove-a-certain-line-from-bash-history-file

^posted in the comments

The below is preserved only for hysterical accuracy


But suppose you KNOW you're about to enter a command you don't want to go into history. It'd be nice if you could just tack a little "hideme" modifer onto the front or tail of your command and be done with it. Unfortunately from what I've been able to google there is no such feature built into history or bash.

Naturally I made one.

TMP=$(history | tail -1 | awk '{print $1}') && history -d $TMP && \
paste_in_shell_and_replace_this_with_whatever_you_want_to_hide

And naturally someone smarter than me came along and found a better way to do it - THANKFULLY they posted a comment here to help us out (thanks Mitch!):

history -d $((HISTCMD-1)) && \
paste_in_shell_and_replace_this_with_whatever_you_want_to_hide

Rather than holding down backspace, you may find it useful to know that in bash Ctrl-W will delete from the cursor to the beginning of the previous word.

What I don't get, is that according man bash HISTCMD should be the CURRENT history number:

HISTCMD
The history number, or index in the history list, of the current command.


and yet in ALL my tests $HISTCMD is the "index in the history list, of the current command" +1

But it can still lead to two two useful aliases:

alias hideme='history -d $((HISTCMD-1))'
alias hideprev='history -d $((HISTCMD-2)) && history -d $((HISTCMD-1))'

Dig the sneaky:

# history
1 cd
2 history
3 ls -alhF
4 history
5 history
6 history -d 5
7 history
8 vi .bashrc
9 history
# echo password && hideme
password
# echo password
password
# hideprev
# history
1 cd
2 history
3 ls -alhF
4 history
5 history
6 history -d 5
7 history
8 vi .bashrc
9 history
10 history


I know the blog's kinda been on Linux kick lately - some of that is coming from the new job - I'm using Linux more. But, I've been working on a little project in Eclipse - Java/SWT - and I'm getting to a point where I may have some useful learnings to post coming out of that. Or maybe not...

Internet Tablet, and all my xbox's are running fine...

I'd like to throw out some props to Ivie for sending me an email about one of my posts that she read. I try to post stuff that I myself have trouble finding out there on the interwebz - so it's always nice to hear from someone that finds it useful - thanks Ivie!

28 comments:

Anonymous said...

Nifty!

Anonymous said...

from man bash
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.

clayg said...

Thank you for that input, although the HISTCONROL environment variable didn't quite suit my needs for the particular application that spawned this article. (I needed something more 'on the fly')

I'm sorry you didn't find what you're looking for here. Thanks again.

Mitch said...

Nice idea, but I think you've made it much more complicated than it has to be.
There is a variable ($HISTCMD) that tracks the history number.
So, to get what you want, all you need is an alias, a la ..

alias hideme='history -d $(($HISTCMD-1))'

No executables, no pipes, nothing but bash.

Example:
-bash-3.00$ alias hideme='history -d $(($HISTCMD-1))'
-bash-3.00$ echo 1
1
-bash-3.00$ echo 2
2
-bash-3.00$ echo password && hideme
password
-bash-3.00$ echo 4
4

-bash-3.00$ history | tail -5
396 alias hideme='history -d $(($HISTCMD-1))'
397 echo 1
398 echo 2
399 echo 4
400 history | tail -5

clayg said...

Mitch,

That is GREAT! That is defiantly the exact environment variable I was surprised did not exist! THANK YOU! I will update my article.

-clayg

Mitch said...

Glad you like it.
By the way, I came upon your blog entry when I was looking for a way to "delete last command from bash history" as the title said.
More often than I care to admit, I accidentally type a password on the command line. I knew about history -d, but it always pained me to have to run `history` just to find out the offset and have it spit out the password yet again.
The solution is a variant on the previous one:
alias hideprev='history -d $(($HISTCMD-2)) && history -d $(($HISTCMD-1))'

bash# alias hideprev='history -d $(($HISTCMD-2)) && history -d $(($HISTCMD-1))'
bash# echo 1
1
bash# echo password
password
bash# hideprev
bash# history 3
531 alias hideprev='history -d $(($HISTCMD-2)) && history -d $(($HISTCMD-1))'
532 echo 1
533 history 3

N.B. The quotes are very important when defining the aliases, otherwise the current numeric value of HISTCMD will get embedded in the alias; not at all what you want.

Also note, I was myself guilty of over-complication when showing the original example. I used `history | tail -5` when `history 5` is all that is needed.
(Man pages are such wonderful things)

Anonymous said...

hi guys! If you prepend a space to a bash command, it will not be appended to the history :) Unfortuantely, i found out, that this feature was implemented to bash quite recently, so it will not work on older versions of bash...

Wolfgang Pfeiffer said...

Quick way to completely switch off commands writing to bash history:
ln -s /dev/null .bash_history

Make def. sure you back up your .bash_history file before doing that ... :)

Credits to linuxsecurity.com

a said...

Thanks for your article.
I discovered by chance that bash command lines starting with a blank character are not stored in history.

Anonymous said...

Nice article! Thanks

Anonymous said...

This helped me a lot. Thanks!

Kjeld Flarup said...

I may be a bug or a feature. But if you start a command line with a space, then it does not go into the history.

James Bigler said...

If you are using hideme, you should use ';' instead of && as the running of hideme will only occur if the first command return an exit code of zero:

echo password;hideme

Example:
$ cat myfile && hideme
cat: myfile: No such file or directory
$ history 2
5025 cat myfile && hideme
5026 history 2
$ cat myfile ; hideme
cat: myfile: No such file or directory
$ history 2
5026 history 2
5027 history 2

Jason said...

If you do not want a line to show up in history, just start with a space.

$(space)somecommand password(enter)

now the command will not show up again if you press up nor in ~/.bash_history. no need for the hideme command.

clayg said...

Yes, when I wrote the article I was not aware of the space. It has been mentioned many times in the comments and is obviously the simplest/built-in/canonical way to accomplish the goal. Pity I wasn't able to find it when I was looking for it originally.

Anonymous said...

space command NOT working in my copy of Ubuntu 11.04

Rushyang said...

No offence clayg..

But didn't you find it a little bit ridiculous to type `hideme && secret_command_here` everytime when you wanted your command not to go in history file? & what if you want to delete a group of commands?

I'd rather do this instead..

1) I would prefer to write whatever and no matter how many commands in terminal.. That would be obviously one session and at the end, (when you close the terminal) that history commands will be appended to ~/.bash_history file
That's why one should remember he can not delete command history from previous session through history -d command.

2) Anyway, When you're done with all your secret commands in terminal... type history press enter..

3) You will be prompted number of history lines and you would know which lines should not be there in ~/.bash_history file when you close terminal. Let's take those lines were 21 to 37 for example.

4) Then type this line before you close your terminal...

for i in {38..21}; do history -d "$i"; done;

press enter

Explanation:
~Reverse loop is necessary.. because if we'd take loop from beginning, for loop wouldn't find second half commands.
~This for loop will take i from end from 38 because this line what you're typing to delete history.. itself is 38th line.

-Regards,
Rushyang Darji

Anonymous said...

still usefull for today. thx

pandeeswaran said...

Good one but I used to use ignorespace for this purpose

Anonymous said...

Thank you very much, this is great

fabiom said...

Pretty interesting blog!
But I would add that
"history -d Offset"
is not enough... you need to type just after:
"history -w"
or
If you close and reopen the shell you have back the full history.. ;)

I got it from:
http://superuser.com/questions/384366/remove-a-certain-line-from-bash-history-file

Anonymous said...

Thank you so much!

Josh said...

Awesome command, I just thought that i couldn't delete it or hide it from everyone !! But just -d parameter does the job well!!

Zhuo Zhang said...

great just what i am looking for

Anonymous said...

Here is another thankful reader. This is the third comment in the past week. Not bad for a blog post 5 years old. Thanks clayg.

Anonymous said...

shut up a kill -9 $$ and your session is closed without saving a history

mahasiswa teladan said...

hi..Im college student, this article is very informative, thanks for sharing :)

Anonymous said...

.
Thanks clayg, it helped.
.