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.

2

9 Answers 9

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.

4
  • 16
    Right and proper. I would like only to put a warning: with !$ you have no full visual control of the line you are running. This can result harmful sometimes. Specially if you incur in a missprint. It takes from the history what to expand. So if you write the last command with a blank space in the beginning, probably this command will not finish in the history. When you will execute your new command with !$ the shell will not take the parameters from the last command line typed but only from the last of the history. Here same more words.
    – Hastur
    Jan 22, 2015 at 15:03
  • 1
    @Hastur You could set your shell to expand history on tab or upon enter to prevent accidents.
    – slhck
    Jan 22, 2015 at 22:05
  • 2
    @Hastur that's why you need to press intro twice in zsh (shameless promotion)
    – Braiam
    Jan 23, 2015 at 23:43
  • @Hastur - When I have any doubt about what's going to happen or I belatedly realize I have to do something else first before running something long I have just typed in, I go to the beginning of the line and add a # or an echo so I can see it and it goes into history so I can get it right back and run it again without the disabling prefix.
    – Joe
    Jan 26, 2015 at 21:13
29

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

6
  • 9
    The delete word shortcut is actually Meta+d, and Meta is usually mapped to Alt on Linux machines. On platforms where this is not the case, an alternative to get the Meta modifier is to use the Escape as a prefix. See en.wikipedia.org/wiki/Meta_key Jan 22, 2015 at 15:04
  • 2
    @GustavoGiráldez if you wanna go deep, then Meta+D is only valid if emacs is enabled (via set -o emacs or simply by default). It won't work in inferior editor-mode or if emacs is disabled.
    – TC1
    Jan 23, 2015 at 1:13
  • In many terminals (because this is a terminal thing and not a Bash thing), the home key will also go to the beginning of a line. Many laptops map Fn + Left to the home key. Similarly, the end key will usually go to the end of the line. I've yet to use a terminal emulator that doesn't support these hotkeys.
    – Kat
    Jan 23, 2015 at 23:52
  • 1
    @Mike On the other hand, C-a and M-d are much faster to type if your hands are already in typing position.
    – jjlin
    Jan 24, 2015 at 1:11
  • @Mike This is a bash thing. Type cat and compare what you get when pressing Home (^[[H here) and when pressing Ctrl+a (^A). Jan 24, 2015 at 13:36
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
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...

4
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 $_
5
  • Those are nice tips, but they don't answer the question.
    – slhck
    Jan 22, 2015 at 9:09
  • @slhck IMHO it does because the question title doesn't match the given examples. If we focus on the examples, as I did, the title should be "How to quickly change a word in a bash command?". This is also why my answer is "in addition to @jjlin" 's one. But I guess the question should be clarified.
    – maiki
    Jan 22, 2015 at 9:21
  • The OP always wants to remove (exchange) the first n words in the command, with n = 1 in the first example and n = 2 in the second. jjlin's answer shows how to delete the first word (and by re-applying the shortcut, delete up to n words). Your answer lists a few Bash shortcuts (which are indeed useful), but none of them are efficient ways to delete the first n words.
    – slhck
    Jan 22, 2015 at 9:27
  • Hm, reading again, the $_ parameter would probably work if there is only one argument (i.e. if n = m - 1 where m is the total number of arguments). The remainder of shortcuts is irrelevant to the question though.
    – slhck
    Jan 22, 2015 at 9:29
  • @slhck For the sake of arguing I've read the first example as "replace the 2nd word" :-)
    – maiki
    Jan 22, 2015 at 9:32
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.

2
  • I learned on kornshell, so set -o vi was how I lived! I even do this in my bash prompts too.
    – bgStack15
    Jan 22, 2015 at 14:48
  • bindkey -v for zsh. Esc 0 c w
    – user530873
    Jan 22, 2015 at 16:29
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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .