45

I would like to improve my workflow a bit with Bash, and I realize I often want to execute the same command to a different executable.

Some examples:

Git (where I would like to quickly change the second word):

git diff     foo/bar.c
git checkout foo/bar.c

Cat/rm (where only the first word has to be changed):

cat foo/is/a/very/long/path/to/bar.c
rm  foo/is/a/very/long/path/to/bar.c

I know I can hit Ctrl+a then Del to remove the first word, but I am wondering if there is a quicker way to do it.

Hastur
  • 19,483
  • 9
  • 55
  • 99
nowox
  • 3,017

9 Answers9

58

!$ expands to the last word of your previous command.

So you could do:

cat foo/is/a/very/long/path/to/bar.c

rm !$

or:

git diff foo/bar.c

git checkout !$

Your examples happened to only repeat the last word, so !$ worked fine. If you actually had a lot of arguments that you wanted to repeat, and you just wanted to change the first word, you could use !*, which expands to all words of the previous command except the zeroth.

See the "HISTORY EXPANSION" section of the bash man page. There's a lot of flexibility there.

Spiff
  • 110,156
29

Ctrl+a to go to the beginning of the line, then Alt+d to delete the first word.

jjlin
  • 16,120
17

I'm not sure if it would actually be faster or not, but see this article, particularly point #.3:

  1. Replace a string from the previous command using ^str1^str2^

In the following example, first we executed the ls command to verify a file. Later we realized that we want to view the content of the file. Instead of typing the whole file name again, we can just replace the “ls” in the previous command with “cat” as shown below.

$ ls /etc/cron.daily/logrotate

$ ^ls^cat^
cat /etc/cron.daily/logrotate
OddOneIn
  • 301
16

Alt+.

If you need only to repeat the last part of one of the previous commands you can use Alt+. that will paste the last argument taken from the last line of the history.

If pressed more than one time will substitute what just pasted with the last argument of the line before...

E.g. if the last 2 commands were:

cat foo/is/a/very/long/path/to/bar.c
pico foo/is/a/very/long/path/to/SomeWhereElse.c

In the shell I write rmand I press Alt+. it will appear:

rm foo/is/a/very/long/path/to/SomeWhereElse.c

if I press again Alt+. it will appear instead

rm foo/is/a/very/long/path/to/bar.c

and so on...

Hastur
  • 19,483
  • 9
  • 55
  • 99
12

In addition to @jjlin answer, you might be interested by the following tips:

  • Ctrl+w deletes the word to your left
  • Ctrl+k deletes from cursor to the end of command line
  • Ctrl+u deletes from cursor to the start of command line
  • Alt+b moves cursor one word backward
  • Alt+f moves cursor one word forward
  • $_ contains last argument of the previous command line

For more about these, lookup "Readline Command Names" section of the bash manpage.

eg:

cat foo/is/a/very/long/path/to/bar.c
rm $_
maiki
  • 431
8

If you are proficient in vi, you can activate vi bindings in bash (most shells?) by using the command set -o vi. Then, you can use the normal vi keys to edit your command. In your first example, you could do ^wcw to delete "diff", then change it to checkout.

It may not be worth learning the vi keybindings just to edit the command line, but if you know them already it's a neat productivity booster.

Chaosed0
  • 181
6

There are many ways to skin this cat!

To replace a specific string, ^old^new^extra will take your previous command, replace old with new, and append extra. You may stop as early as you like; e.g. ^old will replace old with the empty string and append the empty string (essentially deleting old and doing nothing more).

1. ^diff^checkout
2. ^cat^rm

Especially useful variant of this:

for i in `seq 1 10`; do echo something complicated; done
# inspect the output to see if it looks right
^echo

To replace a specific word, you can use !m:n-p to refer to words n through p of the command m (use negative numbers to count back from the current command). You can omit the -p part to refer to a single word, and omit the :m-p part to refer to an entire command. Special forms !! for !-1, !$ for the last word of the previous command, and !* for the arguments (all but word 0) of the previous command are pretty handy.

1. !!:0 checkout !!:2-
2. rm !*

I often use both of these; zsh is especially nice here, as hitting tab will expand the cryptic stuff out to make sure you got it right.

There are also so many ways to do this with line editing. Read up on your shell's keybindings.

-2

Most terminal programs support...

  • Press <Up-Arrow> as needed to redisplay one of the previous command lines from history.
    • Press <Down-Arrow> as necessary to move forward through history list.
  • Press <Left-Arrow> as needed to move cursor to end of second word.
  • Press <Backspace> as needed to erase second word.
  • Type in replacement second word.
    • Press <Right-Arrow> / <Left-Arrow> as necessary to reposition for more changes.
  • Hit <RETURN> to execute the command

If the command line you want was not recent, do a reverse-i-search with <Ctrl-r> as described in question ...
Is there a way of using ctrl-r after typing part of command in bash?
Then edit via the arrow keys as needed.

-3

What I do (Ubuntu/bash):

1) Arrow up hey to return previous command
2) Home key to return to beginning
3) Modify command as needed.

Idiot-proof and especially useful for multi-line commands.

Vorac
  • 627