davideisinger.com

My personal website
Log | Files | Refs | README

index.md (11288B)


      1 ---
      2 title: "Level Up Your Shell Game"
      3 date: 2013-10-24T00:00:00+00:00
      4 draft: false
      5 canonical_url: https://www.viget.com/articles/level-up-your-shell-game/
      6 ---
      7 
      8 The Viget dev team was recently relaxing by the fireplace, sipping a
      9 fine cognac out of those fancy little glasses, when the conversation
     10 turned (as it often does) to the Unix command line. We have good systems
     11 in place for sharing Ruby techniques ([pull request code
     12 reviews](https://viget.com/extend/developer-ramp-up-with-pull-requests))
     13 and [Git tips](https://viget.com/extend/a-gaggle-of-git-tips), but
     14 everyone seemed to have a simple, useful command-line trick or two that
     15 the rest of the team had never encountered. Here are a few of our
     16 favorites:
     17 
     18 -   [Keyboard
     19     Shortcuts](#keyboard-shortcuts)
     20 -   [Aliases](#aliases)
     21 -   [History
     22     Expansions](#history-expansions)
     23 -   [Argument
     24     Expansion](#argument-expansion)
     25 -   [Customizing
     26     `.inputrc`](#customizing-inputrc)
     27 -   [Viewing Processes on a Given Port with
     28     `lsof`](#viewing-processes-on-a-given-port-with-lsof)
     29 -   [SSH
     30     Configuration](#ssh-configuration)
     31 -   [Invoking Remote Commands with
     32     SSH](#invoking-remote-commands-with-ssh)
     33 
     34 Ready to get your {{<dither neckbeard.png "" "inline">}}Grinning caricature with messy hair, glasses, and a scruffy beard.{{</dither>}} on? Good. Let's go.
     35 
     36 ## Keyboard Shortcuts
     37 
     38 [**Mike:**](https://viget.com/about/team/mackerman) I recently
     39 discovered a few simple Unix keyboard shortcuts that save me some time:
     40 
     41   Shortcut             | Result
     42   ---------------------|-----------------------------------------------------------------------------
     43   `ctrl + u`           | Deletes the portion of your command **before** the current cursor position
     44   `ctrl + w`           | Deletes the **word** preceding the current cursor position
     45   `ctrl + left arrow`  | Moves the cursor to the **left by one word**
     46   `ctrl + right arrow` | Moves the cursor to the **right by one word**
     47   `ctrl + a`           | Moves the cursor to the **beginning** of your command
     48   `ctrl + e`           | Moves the cursor to the **end** of your command
     49 
     50 Thanks to [Lawson Kurtz](https://viget.com/about/team/lkurtz) for
     51 pointing out the beginning and end shortcuts
     52 
     53 ## Aliases
     54 
     55 [**Eli:**](https://viget.com/about/team/efatsi) Sick of typing
     56 `bundle exec rake db:test:prepare` or other long, exhausting lines of
     57 terminal commands? Me too. Aliases can be a big help in alleviating the
     58 pain of typing common commands over and over again.
     59 
     60 They can be easily created in your `~/.bash_profile` file, and have the
     61 following syntax:
     62 
     63     alias gb="git branch"
     64 
     65 I've got a whole slew of git and rails related ones that are fairly
     66 straight-forward:
     67 
     68     alias ga="git add .; git add -u ."
     69     alias glo='git log --pretty=format:"%h%x09%an%x09%s"'
     70     alias gpro="git pull --rebase origin"
     71     ...
     72     alias rs="rails server"
     73 
     74 And a few others I find useful:
     75 
     76     alias editcommit="git commit --amend -m"
     77     alias pro="cd ~/Desktop/Projects/"
     78     alias s.="subl ."
     79     alias psgrep="ps aux | grep"
     80     alias cov='/usr/bin/open -a "/Applications/Google Chrome.app" coverage/index.html'
     81 
     82 If you ever notice yourself typing these things out over and over, pop
     83 into your `.bash_profile` and whip up some of your own! If
     84 `~/.bash_profile` is hard for you to remember like it is for me, nothing
     85 an alias can't fix: `alias editbash="open ~/.bash_profile"`.
     86 
     87 **Note**: you'll need to open a new Terminal window for changes in
     88 `~/.bash_profile` to take place.
     89 
     90 ## History Expansions
     91 
     92 [**Chris:**](https://viget.com/about/team/cjones) Here are some of my
     93 favorite tricks for working with your history.
     94 
     95 **`!!` - previous command**
     96 
     97 How many times have you run a command and then immediately re-run it
     98 with `sudo`? The answer is all the time. You could use the up arrow and
     99 then [Mike](https://viget.com/about/team/mackerman)'s `ctrl-a` shortcut
    100 to insert at the beginning of the line. But there's a better way: `!!`
    101 expands to the entire previous command. Observe:
    102 
    103     $ rm path/to/thing
    104      Permission denied
    105     $ sudo !!
    106      sudo rm path/to/thing
    107 
    108 **`!$` - last argument of the previous command**
    109 
    110 How many times have you run a command and then run a different command
    111 with the same argument? The answer is all the time. Don't retype it, use
    112 `!$`:
    113 
    114     $ mkdir path/to/thing
    115     $ cd !$
    116      cd path/to/thing
    117 
    118 **`!<string>` - most recent command starting with**
    119 
    120 Here's a quick shortcut for running the most recent command that *starts
    121 with* the provided string:
    122 
    123     $ rake db:migrate:reset db:seed
    124     $ rails s
    125     $ !rake # re-runs that first command
    126 
    127 **`!<number>` - numbered command**
    128 
    129 All of your commands are stored in `~/.bash_history`, which you can view
    130 with the `history` command. Each entry has a number, and you can use
    131 `!<number>` to run that specific command. Try it with `grep` to filter
    132 for specific commands:
    133 
    134     $ history | grep heroku
    135      492 heroku run rake search:reindex -r production
    136      495 heroku maintenance:off -r production
    137      496 heroku run rails c -r production
    138     $ !495
    139 
    140 This technique is perfect for an alias:
    141 
    142     $ alias h?="history | grep"
    143     $ h? heroku
    144      492 heroku run rake search:reindex -r production
    145      495 heroku maintenance:off -r production
    146      496 heroku run rails c -r production
    147     $ !495
    148 
    149 Sweet.
    150 
    151 ## Argument Expansion
    152 
    153 [**Ryan:**](https://viget.com/about/team/rfoster) For commands that take
    154 multiple, similar arguments, you can use `{old,new}` to expand one
    155 argument into two or more. For example:
    156 
    157     mv app/models/foo.rb app/models/foobar.rb
    158 
    159 can be
    160 
    161     mv app/models/{foo,foobar}.rb
    162 
    163 or even
    164 
    165     mv app/models/foo{,bar}.rb
    166 
    167 ## Customizing .inputrc
    168 
    169 [**Brian:**](https://viget.com/about/team/blandau) One of the things I
    170 have found to be a big time saver when using my terminal is configuring
    171 keyboard shortcuts. Luckily if you're still using bash (which I am), you
    172 can configure shortcuts and use them in a number of other REPLs that all
    173 use readline. You can [configure readline keyboard shortcuts by editing
    174 your `~/.inputrc`
    175 file](http://cnswww.cns.cwru.edu/php/chet/readline/readline.html#SEC9).
    176 Each line in the file defines a shortcut. It's made up of two parts, the
    177 key sequence, and the command or macro. Here are three of my favorites:
    178 
    179 1.  `"\ep": history-search-backward`: This will map to escape-p and will
    180     allow you to search for completions to the current line from your
    181     history. For instance, it will allow you to type "`git`" into your
    182     shell and then hit escape-p to cycle through all the git commands
    183     you have used recently looking for the correct completion.
    184 2.  `"\t": menu-complete`: I always hated that when I tried to tab
    185     complete something and then I'd get a giant list of possible
    186     completions. By adding this line you can instead use tab to cycle
    187     through all the possible completions stopping on which ever one is
    188     the correct one.
    189 3.  `"\C-d": kill-whole-line`: There's a built-in key command for
    190     killing a line after the cursor (control-k), but no way to kill the
    191     whole line. This solves that. After adding this to your `.inputrc`
    192     just type control-d from anywhere on the line and the whole line is
    193     gone and you're ready to start fresh.
    194 
    195 Don't like what I mapped these commands to? Feel free to use different
    196 keyboard shortcuts by changing that first part in quotes. There's a lot
    197 more you can do, just check out [all the commands you can
    198 assign](http://cnswww.cns.cwru.edu/php/chet/readline/readline.html#SEC13)
    199 or create your own macros.
    200 
    201 ## Viewing Processes on a Given Port with lsof
    202 
    203 [**Zachary:**](https://viget.com/about/team/zporter) When working on
    204 projects, I occasionally need to run the application on port 80. While
    205 I could use a tool like [Pow](http://pow.cx/) to accomplish this, I
    206 choose to use [Passenger
    207 Standalone](http://www.modrails.com/documentation/Users%20guide%20Standalone.html).
    208 However, when trying to start Passenger on port 80, I will get a
    209 response that looks something like "The address 0.0.0.0:80 is already in
    210 use by another process". To easily view all processes communicating over
    211 port 80, I use [`lsof`](http://linux.die.net/man/8/lsof) like so:
    212 
    213     sudo lsof -i :80
    214 
    215 From here, I can pin-point who the culprit is and kill it.
    216 
    217 ## SSH Configuration
    218 
    219 [**Patrick:**](https://viget.com/about/team/preagan) SSH is a simple
    220 tool to use when you need shell access to a remote server. Everyone is
    221 familiar with the most basic usage:
    222 
    223     $ ssh production.host
    224 
    225 Command-line options give you control over more options such as the user
    226 and private key file that you use to authenticate:
    227 
    228     $ ssh -l www-data -i /Users/preagan/.ssh/viget production.host
    229 
    230 However, managing these options with the command-line is tedious if you
    231 use different private keys for work-related and personal servers. This
    232 is where your local `.ssh/config` file can help -- by specifying the
    233 host that you connect to, you can set specific options for that
    234 connection:
    235 
    236     # ~/.ssh/config
    237     Host production.host
    238      User www-data
    239      IdentityFile /Users/preagan/.ssh/viget
    240 
    241 Now, simply running `ssh production.host` will use the correct username
    242 and private key when authenticating. Additionally, services that use SSH
    243 as the underlying transport mechanism will honor these settings -- you
    244 can use this with Github to send an alternate private key just as
    245 easily:
    246 
    247     Host github.com
    248      IdentityFile /Users/preagan/.ssh/github
    249 
    250 **Bonus Tip**
    251 
    252 This isn't limited to just setting host-specific options, you can also
    253 use this configuration file to create quick aliases for hosts that
    254 aren't addressable by DNS:
    255 
    256     Host prod
    257      Hostname 192.168.1.1
    258      Port 6000
    259      User www-data
    260      IdentityFile /Users/preagan/.ssh/production-key
    261 
    262 All you need to do is run `ssh prod` and you're good to go. For more
    263 information on what settings are available, check out the manual
    264 ([`man ssh_config`](http://linux.die.net/man/5/ssh_config)).
    265 
    266 ## Invoking Remote Commands with SSH
    267 
    268 [**David**:](https://viget.com/about/team/deisinger) You're already
    269 using SSH to launch interactive sessions on your remote servers, but DID
    270 YOU KNOW you can also pass the commands you want to run to the `ssh`
    271 program and use the output just like you would a local operation? For
    272 example, if you want to pull down a production database dump, you could:
    273 
    274 1.  `ssh` into your production server
    275 2.  Run `mysqldump` to generate the data dump
    276 3.  Run `gzip` to create a compressed file
    277 4.  Log out
    278 5.  Use `scp` to grab the file off the remote server
    279 
    280 Or! You could use this here one-liner:
    281 
    282     ssh [email protected] "mysqldump -u db_user -h db_host -pdb_password db_name | gzip" > production.sql.gz
    283 
    284 Rather than starting an interactive shell, you're logging in, running
    285 the `mysqldump` command, piping the result into `gzip`, and then taking
    286 the result and writing it to a local file. From there, you could chain
    287 on decompressing the file, importing it into your local database, etc.
    288 
    289 **Bonus tip:** store long commands like this in
    290 [boom](https://github.com/holman/boom) for easy recall.
    291 
    292 ------------------------------------------------------------------------
    293 
    294 Well, that's all we've got for you. Hope you picked up something useful
    295 along the way. What are your go-to command line tricks? Let us know in
    296 the comments.