Essential terminal commands for new engineers

04 Feb 2015

These are often called “bash shortcuts” or “bash tips” but, really, these apply to multiple different shell programs. Bash, ZSH (“zee shell”), etc. and any program that uses a library called ‘readline’ in it all work with (most) of the stuff on this page.

There are literally thousands of these kinds of shortcuts that you could adopt as part of your daily workflow but I’m deliberately presenting just the most commonly useful ones that are truly worth your time. My promise to you: learn these by heart and you’ll move twice as fast in the terminal for the rest of your career.

Reusing parts of previous commands

The !$ shortcut

Most of the commands you type look something like this:

$ command_name --some --arguments filename

The file that you’re working with is the most important part of this because you’re probably not just going to do one thing with that file. More likely you’ll call less filename on it to take a look inside or python filename to run it or vim filename to edit it or cat filename to pipe it to some program or maybe grep filename to check if there’s a specific word or phrase inside it.

Because the filename is the last argument it’s worth practicing the terminal shortcut for inserting the last argument of the previous command into your current one. Try this out in your terminal right now to see what I mean:

# Making a file, examining it, giving it an extension, and making sure it has what we need
$ echo "My name is" >> somefile
$ echo "Jack Danger" >> somefile
$ less somefile
$ mv somefile somefile.text
$ ls -lAG somefile.text
$ grep "Jack Danger" somefile.text

How many times did you just type “somefile”? Now try typing out this one:

$ echo "My name is" >> somefile
$ echo "Jack Danger" >> !$
$ less !$
$ mv !$ !$.text
$ ls -lAG !$
$ grep "Jack Danger" !$

The !$ shortcut means “the last argument of the last command I ran”. I use this all day every day. It’s worth teaching your fingers to type those two characters in quick succession because this muscle memory will help you stay in a state of flow and not fall out just because of the tedium of typing and trying to get filenames right. Particularly if instead of somefile you’re working with apache-http-webserver-nightly-2015011223.tgz

The !! shortcut

This one is slightly less commonly used but is really handy in a specific situation. When you’ve typed a command and you needed to run it with sudo you can either do this:

$ rm file_that_I_hate
> rm: Permission denied
$ sudo rm file_that_I_hate

Sure, that’s fine. But to save yourself some typing and, more importantly so you don’t mess up the actual command typing it by hand again, this is better:

$ rm file_that_I_hate
> rm: Permission denied
$ sudo !!

The !! is a fill-in for the entire previous command.

Text Manipulation

Finding text in a file

Grep is your best friend. There are other, faster versions of this program (hint: brew install ag && ag --help) but grep is installed on every unix box everywhere and it’s good enough. The primary way you’ll use grep is to see which files have what you need:

$ grep "Error:" /var/log/*.log

If you have a regular expression you’d like to use you can just run egrep instead of grep:

$ egrep "Error:|http" /var/log/*.log

And, while there are many flags you can use to customize how you use grep, the one I recommend getting familiar with right away is -C (C for “context”). It will print out lines above and below the match for what you’re searching for. So

$ grep -C 1 "Error:" /var/log/*.log

Will show you not just the lines with “Error:” but will show you one line before and after so you can maybe figure out what’s causing the error.

Printing columns of data

This one is deceptively useful and only requires a little bit of arcane syntax (which you can hide away if you want). You’re often going to have data printed to your screen like this:

$ grep "Error:" my.log
> [error] 2015-01-05 12:31:40 Error: SadComputer - [log level: 1]
> [error] 2015-01-05 12:31:41 Error: BrokeComputer - [log level: 1]
> [error] 2015-01-05 12:31:43 Error: NoFunAtAll - [log level: 1]
> [error] 2015-01-05 12:31:48 Error: ThingsAreTwistedAllUp - [log level: 1]

Maybe all you want is the actual error message here. No problem! There’s a program called awk that’s perfect for this. It does a lot of fancy stuff but without anything special it’s really good at splitting input up into columns and letting you pick which ones you want.

The “SadComputer” message in the first line is the 5th column of text (each column is any contiguous set of characters separated by whitespace). Here’s how we just print that column:

$ grep "Error:" my.log | awk '{print $5}'
> SadComputer
> BrokeComputer
> NoFunAtAll
> ThingsAreTwistedAllUp

That syntax (a print $5 inside curly braces inside quotes) is worth memorizing. But if you dont want to you can always throw this into your ~/.bash_profile and you can make yourself a shortcut:

awkc () {
  awk "{print \$$1 }"
}

That defines a new function that formats a call to awk for you. You’d use it like this:

$ grep "Error:" my.log | awkc 5
> SadComputer
> BrokeComputer
> NoFunAtAll
> ThingsAreTwistedAllUp

Try the tab key

I first started using Linux even before I knew how to program because I couldn’t afford a very powerful computer when I was in college. I remember copying and pasting commands out of web pages to try to install programs. Then, inevitably, I’d be typing some filename that was 60+ characters long and a typo was nearly guaranteed. Nobody told me that you could hit the tab key and your shell will automatically complete filenames for you. Not only that, but depending on the context it may even complete long command invocations for you. When in doubt - hit the tab key.

Go back to the directory you were just in

Running cd - will go back to the directory you were in last and typing it again will return you to where you are now. Super useful if you’re switching mostly between just two places. git checkout - does the exact same thing but with two git branches.

Finding files

There’s a program called find that is kind of user-unfriendly. You find files like this:

$ find /some/directory -name filename.text

And the filename has to be exactly filename.text. If you want a fuzzy match you have to add asterisks and quotes:

$ find /some/directory -name "*filename*"

Personally, I find all this to be cumbersome because I’m often just want to type part of the filename and I usually intend to find it just in the current directory. So I use this shell function as a wrapper (again, you can just put this in your ~/.bash_profile) and it’ll get picked up:

name () {
  # If the second argument ($2) is not an empty string
  if [[ -n $2 ]]; then
     dir=$2 # You typed a second argument, it's a directory to search in
  else
     dir=. # No second argument given, let's use the current directory
  fi
  find $dir -name "*$1*"
}

Then you can look for files by name like this:

$ name fonts
# finds anything named "*fonts*" in the current directory
$ name fonts /var/log

There are so many variations of these commands you can play with but, like learning any skill or exploring any new system, I recommend taking things one step at a time. When you find you use one of these commands really frequently in the same way feel free to add more to your ~/.bash_profile so you can save yourself some typing (and typos).

And please do copy freely from my ‘dotfiles’ repo on Github.


Please if you found this post helpful or have questions.