Useful bash tricks: learning from history
Have you ever tried to do something like this?
# echo "eat flaming death!" > /etc/motd
bash: !": event not found
And then Ctrl+P (or Esc k) doesn’t show the offending line again. Many of my friends are frustrated by this seemingly magic behavior of ‘!’, but never bothered to learn about it. If you read info bash, you’ll see that it’s for history substitution: tokens beginning with a bang are expanded to certain parts of command history. There are like four dozens of history expansion designators and most don’t seem useful enough to memorize, which is why so many people choose to ignore history substitution altogether. I use a few of them frequently, however, so I’m listing my top three here for your reading pleasure:
!$-
Expands to the last argument of the preceding command. This is the one I use the most:
$ mplayer 'THE DOS MARTIANS - ANNOYING FILENAME (Window$ Brother$ Remix).wma' $ sudo chmod a+r !$ $ mplayer !$ $ ffmpeg -i !$ annoying-filename.ogg $ ls -lh !$ $ mv !$ /opt/music/crap $ cd !$Looks confusing? It comes quite easily, actually: intuitively, you’re just “operating on the same file as last time” again and again.
!!Expand to the last command line:
$ pgrep aptitude 1234 $ !! # still running? 1234 $ watch !! # seems like it will take a while, let’s wait… $ grep "gay marriage" presidential-discourse.txt [very long output] $ !! | less^<foo>^<bar>Repeats the last command, substituting “bar” for the first “foo” (like
s/foo/bar/in vi). This sounds useful, but I don’t really use it very much.$ sudo apt-get --download-only -y install libpronget-doc-nonfree E: Couldn't find package libpronget-doc-nonfree $ ^pron^porn $ sudo iwconfig wlan1 scan iwconfig: unknown command "scan" # fuck $ ^iwconfig^iwlist [list networks…] $ sudo ifconfig eth1 essid MyHotNeighbor essid: Unknown host # fuck $ ^ifconfig^iwconfig
Maybe you’d like to try some, or read the docs and find out more. In any case, now that you know what’s that all about you can stop cursing the bash developers for the annoying behavior of failed history expansions.
Ha ha, just kidding. Curse you, bash developers!
“^<foo>^<bar> Repeats the last command, substituting “bar” for the first “foo” (like s/foo/bar/ in vi).”
OHMAGA!!!1111 Era isso que eu queria!
aptitude search liblolcats
aptitude install liblolcats
Comment by tabgal — 2008-02-24 10:19:49
Hey! I recently wrote a ‘Definitive Guide to Bash History‘, you might be interested:
http://www.catonmat.net/blog/the-definitive-guide-to-bash-command-line-history/
Comment by Peteris Krumins — 2008-02-26 15:36:59
that’s interesting, thanks.
Comment by leoboiko — 2008-02-26 15:42:31
I believe that ALT . is a shortcut for the last part of the last command too. So instead of
mkdir foo
cd foo
I type
mkdir foo
cd ALT .
and the result is the same
Comment by Matt Doar — 2008-02-26 16:19:20
Ack, insert new line after the mkdir commands
Comment by Matt Doar — 2008-02-26 16:19:41
Newlines added. Alt+. is nice to know, but I think I’ll keep using !$, because of my keyboard layout. The shortcut is actually Meta+., and I keep my Meta key as the Windows key; I find it kind of clunky to press with ‘.’.
Or maybe I’m just rationalizing resistence to change =)
Comment by leoboiko — 2008-02-26 16:55:00
$ mkdir foo
$ cd ALT
-bash: cd: ALT: No such file or directory
doh!
Comment by CR — 2008-02-26 17:31:16
You can also use ! to run the last command beginning with those characters. For example, you can run “grep -i “foo” file” then run ten others commands other than grep and then type “!grep” to run the grep command again. This saves me TONS of time.
Comment by Ramsey — 2008-03-03 07:31:40
More fun commands are: history (to find the number of a command you want to run:
!47 (to run the 47th command) !47:s/search/replace/ (Mix of that and the ^ syntax) !-2 (run the command two previous to the current position in the history
Comment by bob — 2008-03-13 21:21:11
@ CR: it means PRESS the alt key toghether with . (point)
Comment by jon — 2008-03-15 09:48:01
hi,
i would like to have a way to execute last N commands from history. For example – cd dir1 rm file1 cd ../dir2 rm file 2 cd ..
say above are the 5 commands. I would like to execute these 5 commands. How to do it ?
I wrote this shell script – repeat.sh.
!/bin/sh
echo “start” for i in
history | tail -4 | tr -s " "| cut -d " " -f3-do echo $i done echo “end”Facing a small issue with this. The “i” variable is assigned values like – “cd” “dir1″ “rm” “file1″ … i.e the command is sometimes split across spaces and assigned. Instead would like to have the entire line as one command.
Any pointers to achieve this ? OR any builtin in bash to execute last n commands in history.
Comment by Rajeev — 2008-05-06 11:08:07