How can I repeat only a part of a command in bash?

I’d like to know if it’s possible to just repeat part of a command.
I.e. if I do ls /path/to/somewhere -a, I only want to remove ls and -a.

I know that if I do !! it repeats the previous command (appending the last command to whichever command you write before it) and that if I do !$ it includes the last part of the string, but I’d like to know if it’s possible to re-use only the e.g. middle part of the previous command.

Asked By: telometto

||

Sure, use !^

e.g.

$ ls /path/to/somewhere -a
ls: cannot access '/path/to/somewhere': No such file or directory
$ echo !^
echo /path/to/somewhere
/path/to/somewhere
$

Alternatively (incurring an extra keystroke) you could use !:1.

$ ls /path/to/somewhere -a
ls: cannot access '/path/to/somewhere': No such file or directory
$ echo !:1
echo /path/to/somewhere
/path/to/somewhere
$

This is fully documented in the Event Designators and Word Designators sections of the bash man page.

Answered By: steve

Expanding on @steve’s answer to address your general request:

I’d like to know if it’s possible to re-use only the e.g. middle part of the previous command.

You can use multiple word ranges to re-arrange a prior command:

  • !:x, to recover the xth word, first word is 0th
  • !:x-y, to recover the xth to yth word, inclusive

For example:

$ ls /foo /bar -a /baz >/dev/null 2>&1
$ echo !:1-2 !:4
echo /foo /bar /baz
/foo /bar /baz

Note that redirection operators count as individual words, so:

$ ls /foo >/dev/null 2>&1
$ echo !:3 !:2
echo /dev/null >
bash: syntax error near unexpected token `newline'

Word selection by index can be further expanded to work for commands other than the most recent:

  • !-n, select the nth most recent command
  • !string, select the most recent command beginning with string

Thus, combining those you can get:

$ cat /foo -vet >/dev/null 2>&1
$ ls /bar -a >/dev/null 2>&1
$ cp !cat:1 !ls:1-2 >/dev/null 2>&1
cp /foo /bar -a

I tend to think of the command history as an array – rows of commands, columns of words – which makes this kind of navigation natural.

One caution: crafting new commands by suturing parts of old ones can lead to commands having irreversible effect. I recommend placing :p on one of the arguments to display the built command, rather than invoking it.

For example: rm !ls:1-4:p. This will print the final rm command, after inserting the 1st through 4th argument of the most recent ls command.

Answered By: bishop

Bash has a yank-last-arg command for working with your command history:

yank-last-arg (M-. or M-_)

Insert last argument to the previous command (the last word of the previous history entry). With a numeric argument, behave exactly like yank-nth-arg. Successive calls to yank-last-arg move back through the history list, inserting the last word (or the word specified by the argument to the first call) of each line in turn. Any numeric argument supplied to these successive calls determines the direction to move through the history. A negative argument switches the direction through the history (back or forward). The history expansion facilities are used to extract the last argument, as if the !$ history expansion had been specified.

With the default keybindings, you can activate it by pressing Meta..1 That copies the last word of your most recent command to where your cursor is.

To copy the nth word instead of the last word, press Metan, then Meta..

I find that this is easier to use and is less error-prone than history expansion, since it works interactively.


Another way to tweak a previous command interactively is to use the edit-and-execute-command feature:

edit-and-execute-command (C-x C-e)

Invoke an editor on the current command line, and execute the result as shell commands. Bash attempts to invoke $VISUAL, $EDITOR, and emacs as the editor, in that order.

Use the up/down arrow keys to recall the command, then press CtrlX, CtrlE to open that command in your $EDITOR. You can then edit the command to your liking, then when you save and exit the editor, it will execute.


1 Depending on your keyboard layout, Meta could mean holding down an Alt key, holding down an Option key, or pressing and releasing the Esc key. The . is the period or full-stop character, in case you have difficulty reading it here.

Answered By: 200_success

Arrow-up. Then backspace to remove a few chars. Ctrl+A to jump to start of line, DEL to remove a few chars. Visual feedback all the way, no guessing. Ctrl+E to jump back to end of line.

Answered By: Ramon
Categories: Answers Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.