The things he calls "bang commands" are actually "event designators" (!!, !blah), "word designators" ($) and "word modifiers" (:p). There are links to docs for each at the bottom of this page: http://www.gnu.org/s/bash/manual/html_node/History-Interacti...
I use them all the time, and anyone watching over my shoulder always asks what that was. Learn !!, !$, and a few modifiers. You'll be glad you did. Personally, I use !$ constantly (and variations like !-2$, which gets you the last arg from 2 commands ago). The "h" modifier is handy, too. It's like dirname for the word. Consider this example, where you copy a file to some deep path, and then cd to that dir to continue working with the copy:
$ cp foo.txt /some/really/long/destination/bar.txt
$ cd !$:h
$ # (do more with ./bar.txt)
The substitution (:s) and global substitution (:gs) modifiers are also useful:
$ echo some args
some args
$ !!:s/some/more
more args
(Also, note that his explanation of !* is actually incorrect. He says it leaves off the last arg, but actually leaves out $0 (the command name). !* is all of the previous command's arguments.)
Finally, one really useful thing he doesn't mention at all is brace expansion. For example, these are equivalent:
I used to use !$ all the time, but these days I tend to go for M-. which inserts the last argument interactively, and cycles through the last argument of all previous commands (but you don't get modifiers like !$:h).
M-C-y (yank-nth-arg) works similarly for the first argument of the previous command, but without cycling through all previous commands.
After the more complex modifiers like !$:p, :e, :s/old/new/, etc, before hitting Return I often use M-C-e (shell-expand-line) just to check I got it right.
Thank you! The backup tip was great. I never thought of using expansion like that...
You post inspired me to quickly create two (hopefully) handy bash functions:
# Create a backup of the given file(s)
# USAGE: bk file1 file2 file3
# -> result: file1.bak file2.bak file3.bak
bk() {
for file in "$@"
do
cp $file{,.bak}
done
}
# Restores the file from backup
# USAGE: rbk file.bak OR rbk file
# -> result: replaces file with file.bak
rbk() {
if [[ $1 == *.bak ]]
then
backup_file=$1
old_file=${backup_file:0:$((${#backup_file}-4))}
else
old_file=$1
backup_file=${old_file}".bak"
fi
read -p "Replace the old backup? (y/N) " -n 2
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
`cp $backup_file $old_file`
fi
}
A neat thing about csh/tcsh is that you can use history expansion with aliases. There are some legacy aliases at work that allow SQL queries from the command line using this:
$ alias select='sqlcommand -s "select !*"'
would translate:
$ select * from tablename
to:
$ sqlcommand -s "select * from tablename"
(Note: I may be getting the specifics messed up as it's been a couple of years since I looked at the implementation, but the general idea is there.)
A small change that made it much easier for me to use ! commands was enabling magic space, which expands the ! macros out when you press space. This made an enormous difference to me, since I could now be sure I wasn't accidentally completing the wrong command or arguments.
To enable it, edit your .inputrc (this is part of readline) and add this:
!! and !$ are alright, but I really dislike !blah, which is blindly executing the latest command starting with blah, whatever it is. The author of the article should remove this "tip", it is bad advice, or at least make a note that the !blah:p is highly preferable.
I prefer using Ctrl-r <blah>, which searches your history for 'blah' and puts it on the command line. The preferable part is that you can keep hitting Ctrl-r to see previous matches, which is often helpful since you don't want the last match, but the second or third match.
A note to Mac users: Instead of Alt, you must press escape key. For example, Esc+b moves the cursor backward one word in bash, but you'll probably want to use option instead, so check 'Use option as meta key' in Terminal's settings (under keyboard tab).
But if it's not very handy, you can set 'option+left arrow' to move cursor one word backward. To do that, open Terminal's settings, go to keyboard and add a shortcut for left arrow (with 'option' as modifier), and leave the action to 'send string to shell'. Then click on the textbox below that and press esc (which should print '\033') and then 'b', or simply type '\033b' in the textbox.
Just this simple shortcut makes Mac's Terminal 1000 times better.
Thanks very, very much. I'm embarrassed to admit I probably spend 3-5 hours a day on a command line, and have just gotten used to set -o vi when I need to bounce around long lines a lot. esc+f/esc+b were always awkward. It should have occurred to me to just map the keys on Terminal.app!
In the interesting news department - Lion already has option left cursor/right cursor already mapped to move a word forward and back - not sure if it was there in snow leopard.
Great articles. I'm just starting to get into the hang of vi. I'm giving vi-mode in bash a go (with "set -o vi") but there doesn't seem to be any indication of when I'm in command/insert mode. Is there any way to see this?
I'm also a big zsh user, one of the biggest timesavers for me was to get autocompletion of the arguments from the last command working. I use meta-. for the last argument, but !:# syntax is awkward and I don't often remember exactly what number parameter something was. Took a bit of screwing around, but I eventually figured it out and the details are on the unix/linux stack exchange site: http://unix.stackexchange.com/questions/5229/add-arguments-f...
One of my favorite bash shortcuts is binding the up arrow to history-search-backward. This is the first page I found that concisely explains how; the variant in the second comment is the one actually in my .bashrc:
A quick look at the article shows it's binding history search. It's already bind to ctrl-r - you can do a ctrl-r, type a few letters of the long commands, and keep doing a ctrl-r till you find the right match.
Apart from additional bind your setup requires, arrow keys are near impossible to touch type. Even if you can touch type them, that means taking your hands off the home row, and positioning them again.
cd - is also quite cool, it takes you to the previous working directory.
$ pwd
/some/really/long/path/goes/here
$ cd /var/log/app
Work in this directory for a while, then go to previous dir
$ cd -
$ pwd
/some/really/long/path/goes/here
I have the following in my zshrc for sudo. It adds sudo at the beginning of the current line or writes sudo !! if the current line is empty. I've aliased it to alt+s
run-with-sudo() {
if [[ -z $LBUFFER ]]; then
LBUFFER="sudo !!";
else
LBUFFER="sudo $LBUFFER";
fi
};
zle -N run-with-sudo;
bindkey '\es' run-with-sudo
It's well known that !! is a sweet little helper in case where you forgot to sudo a command. However, I find that for those cases I usually use `Up-arrow CTRL-A` to get the previous command and then go to the beginning of the line where I can type `sudo`. Is there any reason to prefer the !! variant?
If you're on a non-interactive terminal. Some of us didn't throw out our teletypes. Also: when rooting boxes often one lacks space in the exploit payload to set up the tty line discipline.
I think it's faster than a cycle of :p, verify it's what you want and then reexec. It's also mentally less taxing (albeit a tiny amount). But summed over a day of shell work, I think it makes a difference.
Most of the commands there can be replaced with vi keybindings.
That is,
set -o vi
You can type that directly into your terminal for use just in one session or in your .bashrc
Type <C-]> or <Esc> just like in vim to escape into "command" mode. Most command mode commands work. E.g. '^' goes to the beginning of the line '$' goes to the end. 'i' sets bash back into insert mode.
There's no need for seq or jot. In bash you can replace `seq 1 10` with {1..10}. Or {01..10} if you want zero padding when using $i to label files.
I use an function called "up" that takes an optional integer argument to go up a certain number of levels. It won't necessarily be faster, but somehow I prefer it to typing lots of dots in a row:
function up () { if test $# = 1 ; then s=$( printf "%$1s" ); s=${s// /..\/}; cd $s ; else cd .. ; fi; }
commandline and vim have easily got the most powerful, yet the most rage-inducing key mappings – ever.
The worst, and most easily rectifiable aspect is the way command pairs (eg move back/forward a word) require two totally separate commands (esc + b/esc + f) instead of having one command + a generic 'reverse' meta key.
In the future I hope someone has the balls to do away with this legacy tripe and popularise some more Donald-Normanesque keyboard shortcuts.
I'm not sure if you're just trolling, but I don't understand your complaints at all. What would your "Donald-Normanesque keyboard shortcuts" even look like? In Windows the arrow keys act pretty similar, don't they? left-arrow, right-arrow for moving by character, Ctrl-left-arrow, Ctrl-right-arrow for moving by word. Windows is about as Donald-Normanesque as you can get...
The bash shortcuts are from readline, which (in this case, but by default?) are the same as Emacs. C-f,C-b for moving by character, M-f,M-b for moving by word. C-n,C-p for next and previous line. I think there's a vi switch for readline which would allow you to have vi style shortcuts in bash. This is nice, because it means you have one set of keyboard shortcuts in your editor and shell. Emacs users in OS X get this in any text box (at least any that supports the same defaults as the system, I'm spitting at you MS Outlook/Entourage). I'm sure you could set something up in other windows systems, like GNOME or KDE.
Sure the keyboard shortcuts might be cryptic, but any keyboard shortcuts will be. Don't slag something just because you don't know the history and background behind the choices.
Sometimes I find myself typing out a long command and then deciding that I don't want to run it quite yet. Normally I just hit C-c run some other command first and then copy paste the long command. Surely there is someway to put the long command in history?
This is another great solution. As a vim user I haven't much looked into the emacs keybindings in readline. I'll have to read up. Also the terminology was a little confusing. I didn't realize that killing a line was the same as "cutting".
Yeah, and of course this does mean you have to be a little careful with M-backspace (backward-kill-word in Emacs), since it will change your kill ring.
But on other hand M-backspace can be useful too if you want to get just part of a line, you can move the cursor to where you want, then M-backspace through words to grab it - if you do M-backspace repeatedly the entire string is pulled into the kill ring until you stop pressing M-BS.
These assume you use default emacs mode. If you are vi/vim user then set -o vi will allow you to get immediately productive in bash, using vi motion commands to edit the command line.
I use them all the time, and anyone watching over my shoulder always asks what that was. Learn !!, !$, and a few modifiers. You'll be glad you did. Personally, I use !$ constantly (and variations like !-2$, which gets you the last arg from 2 commands ago). The "h" modifier is handy, too. It's like dirname for the word. Consider this example, where you copy a file to some deep path, and then cd to that dir to continue working with the copy:
The substitution (:s) and global substitution (:gs) modifiers are also useful: (Also, note that his explanation of !* is actually incorrect. He says it leaves off the last arg, but actually leaves out $0 (the command name). !* is all of the previous command's arguments.)Finally, one really useful thing he doesn't mention at all is brace expansion. For example, these are equivalent:
http://www.gnu.org/s/bash/manual/html_node/Brace-Expansion.h...