Level Up Your Shell Game
The Viget dev team was recently relaxing by the fireplace, sipping a fine cognac out of those fancy little glasses, when the conversation turned (as it often does) to the Unix command line. We have good systems in place for sharing Ruby techniques (pull request code reviews) and Git tips, but everyone seemed to have a simple, useful command-line trick or two that the rest of the team had never encountered. Here are a few of our favorites:
- Keyboard Shortcuts
- Aliases
- History Expansions
- Argument Expansion
- Customizing
.inputrc
- Viewing Processes on a Given Port with
lsof
- SSH Configuration
- Invoking Remote Commands with SSH
Ready to get your on? Good. Let’s go.
Keyboard Shortcuts
Mike: I recently discovered a few simple Unix keyboard shortcuts that save me some time:
Shortcut | Result |
---|---|
ctrl + u | Deletes the portion of your command before the current cursor position |
ctrl + w | Deletes the word preceding the current cursor position |
ctrl + left arrow | Moves the cursor to the left by one word |
ctrl + right arrow | Moves the cursor to the right by one word |
ctrl + a | Moves the cursor to the beginning of your command |
ctrl + e | Moves the cursor to the end of your command |
Thanks to Lawson Kurtz for pointing out the beginning and end shortcuts
Aliases
Eli: Sick of typing bundle exec rake db:test:prepare
or other long, exhausting lines of terminal commands? Me too. Aliases can be a big help in alleviating the pain of typing common commands over and over again.
They can be easily created in your ~/.bash_profile
file, and have the following syntax:
alias gb="git branch"
I’ve got a whole slew of git and rails related ones that are fairly straight-forward:
alias ga="git add .; git add -u ."
alias glo='git log --pretty=format:"%h%x09%an%x09%s"'
alias gpro="git pull --rebase origin"
...
alias rs="rails server"
And a few others I find useful:
alias editcommit="git commit --amend -m"
alias pro="cd ~/Desktop/Projects/"
alias s.="subl ."
alias psgrep="ps aux | grep"
alias cov='/usr/bin/open -a "/Applications/Google Chrome.app" coverage/index.html'
If you ever notice yourself typing these things out over and over, pop into your .bash_profile
and whip up some of your own! If ~/.bash_profile
is hard for you to remember like it is for me, nothing an alias can’t fix: alias editbash="open ~/.bash_profile"
.
Note: you’ll need to open a new Terminal window for changes in ~/.bash_profile
to take place.
History Expansions
Chris: Here are some of my favorite tricks for working with your history.
!!
- previous command
How many times have you run a command and then immediately re-run it with sudo
? The answer is all the time. You could use the up arrow and then Mike’s ctrl-a
shortcut to insert at the beginning of the line. But there’s a better way: !!
expands to the entire previous command. Observe:
$ rm path/to/thing
Permission denied
$ sudo !!
sudo rm path/to/thing
!$
- last argument of the previous command
How many times have you run a command and then run a different command with the same argument? The answer is all the time. Don’t retype it, use !$
:
$ mkdir path/to/thing
$ cd !$
cd path/to/thing
!<string>
- most recent command starting with
Here’s a quick shortcut for running the most recent command that starts with the provided string:
$ rake db:migrate:reset db:seed
$ rails s
$ !rake # re-runs that first command
!<number>
- numbered command
All of your commands are stored in ~/.bash_history
, which you can view with the history
command. Each entry has a number, and you can use !<number>
to run that specific command. Try it with grep
to filter for specific commands:
$ history | grep heroku
492 heroku run rake search:reindex -r production
495 heroku maintenance:off -r production
496 heroku run rails c -r production
$ !495
This technique is perfect for an alias:
$ alias h?="history | grep"
$ h? heroku
492 heroku run rake search:reindex -r production
495 heroku maintenance:off -r production
496 heroku run rails c -r production
$ !495
Sweet.
Argument Expansion
Ryan: For commands that take multiple, similar arguments, you can use {old,new}
to expand one argument into two or more. For example:
mv app/models/foo.rb app/models/foobar.rb
can be
mv app/models/{foo,foobar}.rb
or even
mv app/models/foo{,bar}.rb
Customizing .inputrc
Brian: One of the things I have found to be a big time saver when using my terminal is configuring keyboard shortcuts. Luckily if you’re still using bash (which I am), you can configure shortcuts and use them in a number of other REPLs that all use readline. You can configure readline keyboard shortcuts by editing your ~/.inputrc
file. Each line in the file defines a shortcut. It’s made up of two parts, the key sequence, and the command or macro. Here are three of my favorites:
-
"\ep": history-search-backward
: This will map to escape-p and will allow you to search for completions to the current line from your history. For instance, it will allow you to type “git
” into your shell and then hit escape-p to cycle through all the git commands you have used recently looking for the correct completion. -
"\t": menu-complete
: I always hated that when I tried to tab complete something and then I’d get a giant list of possible completions. By adding this line you can instead use tab to cycle through all the possible completions stopping on which ever one is the correct one. -
"\C-d": kill-whole-line
: There’s a built-in key command for killing a line after the cursor (control-k), but no way to kill the whole line. This solves that. After adding this to your.inputrc
just type control-d from anywhere on the line and the whole line is gone and you’re ready to start fresh.
Don’t like what I mapped these commands to? Feel free to use different keyboard shortcuts by changing that first part in quotes. There’s a lot more you can do, just check out all the commands you can assign or create your own macros.
Viewing Processes on a Given Port with lsof
Zachary: When working on projects, I occassionally need to run the application on port 80. While I could use a tool like Pow to accomplish this, I choose to use Passenger Standalone. However, when trying to start Passenger on port 80, I will get a response that looks something like “The address 0.0.0.0:80 is already in use by another process”. To easily view all processes communicating over port 80, I use lsof
like so:
sudo lsof -i :80
From here, I can pin-point who the culprit is and kill it.
SSH Configuration
Patrick: SSH is a simple tool to use when you need shell access to a remote server. Everyone is familiar with the most basic usage:
$ ssh production.host
Command-line options give you control over more options such as the user and private key file that you use to authenticate:
$ ssh -l www-data -i /Users/preagan/.ssh/viget production.host
However, managing these options with the command-line is tedious if you use different private keys for work-related and personal servers. This is where your local .ssh/config
file can help – by specifying the host that you connect to, you can set specific options for that connection:
# ~/.ssh/config
Host production.host
User www-data
IdentityFile /Users/preagan/.ssh/viget
Now, simply running ssh production.host
will use the correct username and private key when authenticating. Additionally, services that use SSH as the underlying transport mechanism will honor these settings – you can use this with Github to send an alternate private key just as easily:
Host github.com
IdentityFile /Users/preagan/.ssh/github
Bonus Tip
This isn’t limited to just setting host-specific options, you can also use this configuration file to create quick aliases for hosts that aren’t addressable by DNS:
Host prod
Hostname 192.168.1.1
Port 6000
User www-data
IdentityFile /Users/preagan/.ssh/production-key
All you need to do is run ssh prod
and you’re good to go. For more information on what settings are available, check out the manual (man ssh_config
).
Invoking Remote Commands with SSH
David: You’re already using SSH to launch interactive sessions on your remote servers, but DID YOU KNOW you can also pass the commands you want to run to the ssh
program and use the output just like you would a local operation? For example, if you want to pull down a production database dump, you could:
-
ssh
into your production server - Run
mysqldump
to generate the data dump - Run
gzip
to create a compressed file - Log out
- Use
scp
to grab the file off the remote server
Or! You could use this here one-liner:
ssh user@host.com "mysqldump -u db_user -h db_host -pdb_password db_name | gzip" > production.sql.gz
Rather than starting an interactive shell, you’re logging in, running the mysqldump
command, piping the result into gzip
, and then taking the result and writing it to a local file. From there, you could chain on decompressing the file, importing it into your local database, etc.
Bonus tip: store long commands like this in boom for easy recall.
Well, that’s all we’ve got for you. Hope you picked up something useful along the way. What are your go-to command line tricks? Let us know in the comments.