20

In UNIX systems you can press top and bottom arrows to navigate through the previous commands. This is extremely handy.

Sometimes, I go up and find a command that I want to use again, but with some variations. If I do such changes, then I don't have a way to get the original command back, unless I check it in history.

Is there any way to "undo" the changes to the command in the history accessed through keys?

My current workaround is to prepend a # to the command. This way the current command is performed as a comment, so nothing happens. Then, I can browse again through the commands with the keys. The problem is that the command I was using may be veeeery far away in the list, so going up again two hundred times is a bit . Control + R is not a solution either, since I may not remember exactly what I was looking for.

Example

I typed the following "50 commands ago":

ls -l /etc/httpd/conf/

Now I went up to that line and changed it to

ls -l /etc/init.d/

but did not press enter. Now, I want to get to the ls -l /etc/httpd/conf/ again.

My environment

$ echo $SHELL
/bin/bash
$ echo $TERM
xterm
iyrin
  • 1,895
fedorqui
  • 7,861
  • 7
  • 36
  • 74
  • That the command history is changed is likely a (mis-)feature of the shell you are using. In ksh, for example, you can edit history entries and invoke them without deleting the "previous version" of the entry; you have all invoked commands available. – Janis Apr 30 '15 at 12:16
  • With "it changes" I mean that: given a previous command that I changed after reaching it with the arrows, if I go up and down with the arrows, the old command won't show the way it was, but the way I modified it. Of course, if I type "enter" performing a command, that old command will be as it was initially; but this is precisely what I am trying to avoid and looking for a way to "reset" the command to its initial value. – fedorqui Apr 30 '15 at 12:27
  • 1
    Also in this case, ksh's behaviour differs here, it seems. And your history edit options also depend on the choosen editing mode. In ksh's vi-mode, for example, the old entry is not overwritten, and I can also issue an undo command, like in vi, with 'u'. – Janis Apr 30 '15 at 12:33
  • Uhms, this sounds very promising. I am now working in Bash and currently not considering to change to ksh, but it is good to know such thing exists there. – fedorqui Apr 30 '15 at 12:50
  • I'd like to know where bash stores recent command history before writing to the history file when the shell is closed. It may help in determining if this is possible. Anybody? I thought it might be in a one of the shell variables, but don't have time to pore over the manual right now. – iyrin Apr 30 '15 at 22:32
  • Possible duplicate of http://unix.stackexchange.com/questions/131657/is-there-any-way-to-undo-a-bash-history-modification – Timothy Martin Apr 30 '15 at 22:42

3 Answers3

19

As long as you've edited a history entry but not pressed Enter yet, to go back to the original entry, repeatedly press Ctrl+_ — the undo command — until it doesn't make any further change. You're back to the original entry.

10

Revert to the original command when you've have made multiple changes to the line:

revert-line (M-r) 
    Undo all changes made to this line. This is like 
executing the undo command enough times to get back to the beginning.

M is the meta key, which is alt</kbd+r for me.


Gratuitous, Yet Helpful Info

When you have executed a command like OP had previously done (it happens), there is nothing to "undo" because the commands are handled and remembered by the GNU Readline library and are not written to $HISTFILE until the shell exits. The reason I mention this is because you can't just grep the $HISTFILE like one might expect and it may not be ideal to exit the shell.

So here are a couple of options to save you from scrolling back through all previous readline commands.

If you remember some of the command, press ctrl+r and type what you remember to search through previous commands containing that string. Pressing ctrl+r again will display the next most recent, searching backwards.

For example, type ls -l then ctrl+r as many times as it takes to find the previous command you seek. If you scroll past it, ctrl+s will search forward from the current position.

The fc bash builtin command is helpful to list the index numbers alongside previous readline commands.

fc -l -100 will list the previous 100 commands in readline.

Additionally, if OP knows he's looking for a previous ls -l command, he could pipe the output to grep such as: fc -l -100 | grep 'ls -l'

This should output a list of previous ls -l commands preceded by an index number. The output looks like this:
2065 ls -l

Now you can use the event designator !n where n is the index number. In this example, executing !2065 will expand to ls -l.


Although it doesn't help after the fact, anyone looking to preserve the recent command history when modifying previous commands should see the HISTORY EXPANSION section of man bash. Here are some alternative methods for modifying old commands without overwriting the history.

The Event Designators section shows how you can easily edit string from the most recently used command.

^string1^string2^
              Quick  substitution.  Repeat the previous command, replacing string1 with string2.  Equivalent to
              ``!!:s/string1/string2/'' (see Modifiers below).

Example:

$ echo foo
foo 
$ ^foo^bar
echo bar
bar

The most recent commands will now show:

echo foo
echo bar

The above example also explains how to use sed to replace a string in an event designator.

!-n    Refer to the current command minus n.

So if 3 commands up in the history is echo foo then you would use:
!-3:s/foo/bar

Note that event designators will appear in your history as the command executed, in this case echo bar. It will not appear in the history as !-3:s/foo/bar.

Just throwing that out there as it seems closely related, even if it is more of a "don't do" than the "undo" solution OP is looking for.

iyrin
  • 1,895
  • This is nice, many thanks for the detailed information. I am accepting Gilles's answer since it does exactly what I am looking for, whereas here you assume I know many things about the command I want to redo, which is not always the case. – fedorqui May 01 '15 at 09:18
  • Yeah, I agree. It would be great if that undo feature were extended to previously I entered commands somehow. – iyrin May 01 '15 at 14:07
3

The readline variable revert-all-at-newline, available since Bash 4.0/Readline 6.0, accomplishes nearly the desired effect.

If set, then hitting enter on any line (could be a different command, or even an empty line) will revert any history entries that were edited but not executed (as in the example in the question).

This can be set with

set revert-all-at-newline on

in ~/.inputrc.

or

bind 'set revert-all-at-newline on'

in ~/.bashrc.