code-for-a-living February 12, 2020

When laziness is efficient: Make the most of your command line

DevOps job posts often ask for automation skills, which is a positive way of asking for someone who’s professionally lazy in a way that results in efficiency. The good news is that developers can also learn a few tricks from the land of ops to make their days easier and their work better.

A terminal is never just a terminal. An elaborate prompt can mean someone digs deeply into optimizing the tools she uses, while the information it contains can give you an idea of what kind of engineering she’s done. What you type into the command line can tell you about environment variables, hidden configs, and OS defaults you never knew about. You can make it speak shorthand only known to your terminal and you. And all of it can help you work more efficiently and effectively. 

Bash (a term used for both the Unix shell and the command language; I’ll be using the second meaning in this post) is usually a skill mentioned only in job descriptions for site reliability engineers and other ops jobs. Those same job posts often ask for automation skills, which is a positive way of asking for someone who’s professionally lazy in a way that results in efficiency. The good news is that developers can also learn a few tricks from the land of ops to make their days easier and their work better. 

Your own personal(ized) terminal

There are lots of ways to customize your command line prompt and terminal to make you more efficient at work. We’ll start with possibly the most powerful one: meet ~/.bashrc and ~/.bash_profile

This file exists under several different names, depending on your OS and what you’re trying to accomplish, and it can hold a lot of things that can make your life easier: shorter aliases for common commands, your custom PATH, Bash functions to populate your prompt with environment information, history length, command line completion, default editors, and more. With a little observation of your terminal habits (and a little knowledge of Bash, the command language used in many terminals), you can put all kinds of things in here that will make your life easier. 

Which file you use depends on your OS. This post gives a rundown on the purposes and tradeoffs of the two files. If you use a Mac, though, use ~/.bash_profile. Run source ~/.bash_profile once you’ve saved your changes, so they’re live in your terminal (or just close your terminal window and open a new one).

What else should you put in your beautifully customized new file? Let’s start with aliases.

When automating things in ops work, I watch what operations I do more than a couple of times, make notes on what I did, and put those on a list of likely script ideas. Once it’s clear I’ll be doing it again and again, I know it’s worth the time to put a solution into code. You can do the same thing with your own habits. What commands are you typing all the time? What values are you frequently using? These can all be aliases. 

For example, git commit and git checkout can become gc and gco (or whatever matches your mental map of abbreviations). But you can go further than that by aliasing long commands with lots of flags and arguments. Here’s how to make one:

alias $preferredAlias='$commandToAlias'

alias is the Bash command here (you can make an alias directly on the command line too, and it will only be available for that session until you close that terminal). $preferredAlias is your nice, short name for $commandToAlias, the longer, more cumbersome command you’re typing all the time. No spaces around the = and don’t forget the single straight quotes around the command you’re aliasing. You can also chain commands together using &&. Ever sat next to someone whose command line navigation was completely opaque because they’d optimized their work into a flurry of short aliases? Now you can be that person, too. 

Here are a couple I use:

  • mkcd='mkdir $1 && cd $1' (consolidating a common pair of operations; the $1 takes the first argument, in this case the new file you want to cd into)
  • tfplan='terraform init && terraform plan' (preventing a common mistake for me; this can be used to chain any two commonly paired commands)

If you frequently work across different OSes (varying flavors of Linux, Mac OS), you can go a little further by creating multiple tailored dotfiles that assign slightly differing commands that achieve the same thing to the same alias. No more remembering the minute differences that only come up every month or two—it’s the same couple of characters wherever you are. If you’re prone to misspelling commands (looking at you, gerp), you can alias those too. 

Now let’s look at another capability of dotfiles: customizing your prompt. 

A constant source of truth on the command line

Your terminal prompt is one of the places you can be kindest to yourself, putting what you need in there so you don’t have to type pwd all the time or wonder exactly how long ago you typed that fateful command. At a minimum, I suggest adding a timestamp with minutes to it; that way, if you need to backtrack through recent work to tie cause to effect, you can precisely anchor an action’s time with minimal work. Beyond that, I also suggest adding your working directory and current git branch. My go-to tool for setting this up inexpensively is EzPrompt, which lets you drag and drop your desired prompt elements and returns the Bash you need to add to ~/.bash_profile. It’s a good, simple start when you’re first cultivating your dotfiles. 

If you want to get a little more involved, you can try something like Powerline, which looks slick and offers more involved status information. And if you want to roll your own, self-educate about how to work with colors in the terminal and the elements you can add to your prompt. There’s a whole galaxy of options out there, and Terminals Are Sexy provides guidance to some of the constellations you can explore. Hand-crafted customization is a great way to get used to Bash syntax. If you’re looking to do something more complex with a lengthier command, Pipeline provides an interactive environment to help you refine your output, showing you what your command produces as you edit it.

Once you’ve gotten your file how you like it, do the extra step of creating a personal dotfiles repo. Keep it sanitized (so no keys, tokens, or passwords), and you’ll have safe access to your familiar prompt and whatever other settings you love at every new computer you work on. 

You’ve made your prompt your friend. Next, let’s look at making what comes after that into an ally too. 

The just-enough approach to learning Bash

Bash can be a lot, even when you deal with it every day (especially if some of the codebase comes from someone with an aversion to comments). Not every dev must know Bash, but every dev will benefit from knowing at least some. If nothing else, it helps you understand exactly what’s happening when you use some long, pasted wget command to install a new program. 

The good news is that, with a few strategies, you can navigate most of the Bash you’re likely to encounter without having to become an expert. One of my favorite tools is Explainshell. It can be difficult to get a good, succinct, and completely relevant explanation for what a sample Bash command means, particularly when you get four or five flags deep into it. Man pages are always a good place to start, but Explainshell is an excellent complement. Paste in your command, and the site breaks down each piece so that you actually know what that long string of commands and flags from that seven-year-old Q&A does. 

Sometimes, half the work of navigating the command line is figuring out what subcommands are available. If you’re dealing with a complex tool (looking at you, AWS CLI) and find yourself referring to the docs more often than you’d like, take a minute to search for an autocomplete feature for it. Sometimes autocomplete is available as a separate but still official package; other times, a third party has made their own complementary tool. That’s one of the joys of the command line: you will rarely encounter a problem that’s unique to you, and there’s a good chance someone has been annoyed into action and fixed it.

If you end up continuing to work with the command line (and I hope you do), getting acquainted with pipes demystifies a lot of this work. A pipe in Linux is when you use the | symbol to chain together commands, piping output from one to another. In Unix and Linux, each tool was designed to do one thing well, and these individual tools can then be chained together as needed to satisfy more complex needs. This is a strategy I use a lot, particularly when I need to create and sift through output in the terminal. 

My most common pipe involves adding | grep -i $searchTerm after a command with long output I’d prefer not to pick through manually, if I’m only searching for one thing. (You can use -A and -B to add lines before and after for context, with the number of lines you want as a parameter after each flag. See the grep man page to learn more.) 

Also useful: piping the output to less, which is better if I do want to scroll through the whole output, or at least navigate it and search within the open file, using /$searchTerm, n to see the next entry, and N to see the previous. You can also use cut or awk to manipulate the output, which is particularly useful if you need to create a file of that output with a very specific format. And if you find yourself parsing JSON output much, getting acquainted with jq can save you some time.

Let’s look at some of the other conveniences the command line offers. sudo !! repeats your previous command with sudo pasted in front of it. (The !! is Unix/Linux shorthand for “the previous command” and can be used in other situations too.) So if you ran something fairly involved but forgot that it needed root-level permissions, just use sudo !!. Similarly useful: !$, which gives you the value of the first argument of the previous command, so ls ~/Desktop and cd !$ would show you the files in ~/Desktop and then move you to that directory. And if you need to return to your previous directory and don’t remember the whole path, just type cd - to back up one cd move. 

Faster navigation in text

Here’s a seemingly simple thing I learned a few years ago that regularly startles even long-tenured engineers. Did you know that you can click into the middle of a line in your terminal? Alt-click will move your cursor to where you need to go. It still requires moving your hands off the keyboard, so it’s a little clunky compared with some keyboard navigation. But it’s a useful tool, and oddly impressive—I’ve stunned people by doing that in front of them and then got the joy of sharing it with them. Now you know it too. 

The keyboard shortcut methods of moving your cursor can be equally impressive, though. You can get a lot of mileage out of terminal keyboard shortcuts (to say nothing about making your work a little easier). You can jump to the beginning or end of the line with ctrl-A or E, cut the line from your cursor to the beginning of the line with ctrl-U, or delete the previous word with ctrl-W. Here’s Apple’s long list of keyboard shortcuts for the terminal, which generally work on a Linux command line too. I suggest picking a couple you want to adopt, writing them on a sticky note and putting it on your monitor, and making yourself do it the new way until it feels natural. Then move to the next commands you want to commit to muscle memory, and soon enough, you too can be very efficient… if very confusing to watch for those who don’t work this way. (But then you get to do the kind thing of teaching them the thing you just learned, and the cycle continues.)

Time travel, terminal style

If you only need to refer to your last command, !! or just arrowing up and down are great, straightforward options. But what if you need to dig deeper into the past? To search your terminal history, type ctrl-R and then begin typing. Want to see the whole thing? Just type history

The Mac default is 500 history entries, which is not that much for a heavily used terminal. You can check your history length with echo $HISTFILESIZE. Want to increase its retention? Time to edit ~/.bash_profile again. Just set HISTSIZE and HISTFILESIZE to a very large number—10000000 is a good option. Add export HISTSIZE=10000000 and export HISTFILESIZE=10000000 to ~/.bash_profile (and don’t forget to source ~/.bash_profile again or open a new terminal window for it to take effect). For more details on the difference between these two variables, check out the accepted answer here

Now that your history is (more) infinite, it might be good to know how to clean it up. It lives at ~/.bash_history, which means you can delete it entirely with rm ~/.bash_history.

But let’s look at some of the other information accessible via the command line: environment variables. 

Your terminal’s hidden values: revealed!

Environment variables can come from many different places. Some are just part of your OS; you can see some common ones here. Others may be put in place via ~/.bash_profile when you set them yourself in the terminal or via config or other files run on your system. It’s quick and easy to type echo $varName in the terminal and see if a specific value is set, but what if you don’t know what variables have been set? That’s where set, printenv, and env come in. 

These three programs overlap some in output but aren’t identical. Here’s a quick rundown: 

  • set is more complete and will include variables you’ve set in addition to the ones inherent to your environment. 
  • printenv and env offer similar output of built-in environment variables, but env has more robust capabilities beyond printenv’s simple display purposes, including running a program in a modified environment. The accepted answer here provides some deep history about the existence of both commands and how and why they differ. 

You’ll likely get what you need with set, though. The output is longer, which means you’re more likely to need to pipe to grep or less, but it’s also more likely that you’ll find what you’re looking for. 

Better living through ops skills

You’ve learned how to customize your command line and make it friendlier for troubleshooting. You’ve learned how to unearth surprise values hiding in your local environment variables. You’ve learned some of how to look like a wizard with aliases and keyboard shortcuts. And I bet you can start spreading the good word of ~/.bash_profile. There’s more to Bash and terminal tricks than we’ve laid out here, but it’s yours to discover online—or just ask your friendly local ops engineer out for coffee and ask them their favorite terminal customization. You’ll probably learn more than you expect.

Tags: , , , ,
Podcast logo The Stack Overflow Podcast is a weekly conversation about working in software development, learning to code, and the art and culture of computer programming.


code-for-a-living June 10, 2020

The rise of the DevOps mindset

DevOps has become one of those buzzwords with many conflicting definitions. What’s for certain is it’s on the rise. In our 2020 developer Survey, around 80% of the respondents believed that DevOps is at least somewhat important. We take a look at the phenomenon, some definitions, and talk to our engineers about some of the…
Avatar for Medi Madelen Gwosdz
Content Strategist
Stack Overflow podcast logo
podcast September 25, 2020

Podcast 272: Pull requests are welcome

What gives you that special feeling: a nice, sharp recursive function or a deep, winding ternary statement? Paul and Sara debate the finer points of feeling smugly satisfied with your own code.
Avatar for Ben Popper
Director of Content