How I Learned to Stop Worrying and Love the Terminal
This post is a reflection on how my view of the terminal changed from
intimidating and esoteric to $HOME
sweet $HOME
. The terminal has become an
incrediby productive tool for myself that I use all the time. The reasons why
you would want to use a terminal instead of GUI-based tools are many.
- The ability to automate repetetive tasks
- Many GUIs for tools like
git
are missing features that are available at the command line - Full customizability. You can create the perfect tool for you
- A mouse-free workflow. Once you cut the cord you will notice how clumsy it is for most tasks
- Compared to using an IDE, you will gain a better understanding of how your environment works
Despite being ancient technology by computing standards, it feels like the terminal is going stronger than ever, with tons of new and exciting tools and growing communities like r/unixporn pushing the boundaries of what’s possible in a shell.
I’m going to talk about my history with the termial, what key insights helped me gain a better understanding of Linux, as well as some of my favourite command-line tools and hacks.
My terminal history
I was always a Windows defender, having used it since I was a child, mostly to play games. I had dabbled a little with Java, wanting to make the next Minecraft, but with little success. During that time, I had occasionally opened CMD, mostly by accident, but it was always promptly closed.
It was first when I started university that I encountered Linux, and Linux fan boys, for the first time. On my first day I had a stroke of back luck, and everyone I talked to seemed to only want to talk about their favourite distributions and what the benefits of Ubuntu were compared to Red Hat. I thought they were crazy. Thankfully I found a circle of friends who didn’t care about Linux either.
Soon after we had a Linux introduction course. We learned that cd
changes the
current directory (Linux for folder), mv
and cp
moved and copied files
around and that we could use emacs
to edit text files. It all seemed like a
convoluted file explorer. I still didn’t get the point. There were so many
commands with loads of options and the man
-pages just made me more confused.
If I ever needed to edit text files, I could just use notepad (oof, I know…).
A few years later I took a course in high-performance computing as part of my masters. The course had just become mandatory for a couple of popular masters programs and Big Data (TM) was all the rage. The course had gone from ~30 dedicated students the year prior, to over 150 students, most of whom had never touched a compiler, myself included. It was obvious that the professor was not very pleased.
In the course we used C on a remote machine where we could not install our own
software, leaving little room for anything other than a 100% terminal-based
workflow, and it was rough. I was lucky enough to team up with an acquaintance
with more Linux experience, and I ended up learning a great deal. Again, we used
emacs
for file editing and we used tmux
to emulate several terminals inside
the same window, so that we could edit the code and run the compiler
side-by-side. We also used a little program called git
to cooperate on the
code.
It was first after I started my first job after university that the terminal became a core tool in my arsenal. We were working on a small deployment tool based on docker that we developed through WSL (Windows Subsystem Linux). I mostly ran commands in the integrated terminal in VSCode, like tests and linters, docker, and basic git.
Over time I started to become quite comfortable in the Linux ecosystem inside the little VM that is WSL. I understood now what the Linux fan boys were talking about, and I wanted to dive deeper. For a while now, I have solely been using Linux for work and I’m not looking back.
The enlightenment
There were a number of insights that made a large difference in my understanding
of the terminal and Linux in general. I still think of the terminal more or less
as an advanced file explorer, which is perfect, because almost everying in Linux
is a file. The commands, like ls
and cd
, are simply executable files that
exist in a directory where Linux knows to look for programs. These directories
are found in an environment variable called $PATH
.
Environment variables are tied to the process an application is running on and
can be accessed with $VAR
in your shell. You can set environment variables for
the current session with export VAR=value
and you can see all environment
variables with printenv
. The exported environment variable will be gone next
time you start a terminal. To make it persistent, you need to add the export
statement to .bashrc
or .profile
in your home directory. These files are run
every time you start a new terminal, so the environment variable will always be
set.
Every user on a Linux machine has a username and one or more groups. Permissions
can be set so that only certain groups or users can read, write or execute
certain files. Many commands and files outside of your home directory can only
be accessed by the root user. You can use sudo <command>
to run a single
command as if you were the root user. Do this with caution, however, since you
might expose vulnerabilities or even break your system if you are not careful.
Almost every Linux distribution will have a package manager that you can use to install programs easily from the command line (or with a GUI). In my opinion, these package managers are one of the main benefits that Linux offers over other operating systems. You will rarely need to visit third party websites to install new programs. In addition, the package manager handles your updates and the programs are tested so you can be confident that the packages will work together.
On Debian-based distros, like Ubuntu, you have the apt
package manager. To
install a program you simply type sudo apt install firefox
and then start it
with firefox
. You can uninstall and remove related files with
sudo apt purge
. Doing it this way will avoid the bloat that is commonly left
behind uninstalled Windows programs.
One part of the Unix philosophy
states that programs should be written to work together. In a shell, combining
tools is much easier than in graphical applications, since every program uses
text as both input and output. This allows us to chain commands together, using
the output of one tool as the input to the next until we get our final result.
This is called piping and is achieved with the |
character. Piping is an
incredibly powerful mechanism for combining tools and automating processes that
would otherwise take several commands.
Programs that are run on the command line have three data streams connected to them.
- STDIN (0) - Standard input. The data that is fed into the program
- STDOUT (1) - Standard output. The data that output by the program into the terminal
- STDERR (2) - Standard error. Error messages, also outputs to the terminal
Piping is one way in which the streams of different programs can be combined
together. Another mechanism is redirection, that is able to take the output
from one stream and send it to another. To redirect STDOUT to a file you use the
>
character. man ls > ls-manual.txt
will create a text file with the
contents of the ls
manual. Redirecting another command’s output to the same
file will overwrite its contents, but you can append instead with >>
.
Finally, it’s useful to be familiar with the Linux file system. The file system
is structured like a tree, where the root directory is called /
. The directory
you will most often interact with is your $HOME
directory, located at
/home/username
, this is where all of your private files are stored. The tilde
~
character can be used as a shorthand for the home directory. Other
directories that are good to know are
/usr/bin
: Contains system-wide executables/etc
: Contains system-wide configurations/mnt
: Contains mounted storage, like hard drives
Unlocking the productivity
The command-line takes some getting used to and there are many things to learn. However, once you’re armed with the insights listed above, comfortable navigating the file-system and editing files, I believe the biggest hurdles are behind you. Now you’re ready to get to the good parts!
Discoverability
Discovering what’s possible and how to do it can be tricky. Many commands will
have a man
-page, which is a manual that can be accessed with man <command>
.
When I started out I thought these manuals were very hard to read, but they have
become more helpful over time. Another option is to use the --help
flag that
exists for most commands, that I’ve found often gives more digestible
information.
I want to highlight a useful tool called tldr (too long
didn’t read), which is a community-driven effort to create tiny cheat-sheets for
common command-line tools. For example, tldr tar
will show a list of common
ways to use the tar
command with simple explanations of what the commands do.
Below is the first couple of examples:
- [c]reate an archive and write it to a [f]ile:
tar cf path/to/target.tar path/to/file1 path/to/file2 ...
- [c]reate a g[z]ipped archive and write it to a [f]ile:
tar czf path/to/target.tar.gz path/to/file1 path/to/file2 ...
Shell history
Your shell keeps a history of the commands you’ve run. You probably know that by pressing the up-arrow to you can navigate your most recent commands, but this quickly becomes annoying. By pressing CTRL-R you are able to search your shell history to find commands you ran months ago. This is really useful when you know you’ve run a command before, but you cannot remember the exact options, or to save keystrokes running a common command.
You can supercharge the shell history search with a nicer interface and fuzzy
search with fzf, which I cannot recommend
enough. During installation it will automatically replace CTRL-R to use fzf
instead of the default search. But fzf can do much more than that! Personally, I
use it to search $HOME
to cd
to far-away directories, list tmux
sessions
to attach to, and git
branches to checkout.
Aliases
You will probably find yourself reaching for the same command over and over, and
then it is a good idea to create an alias
for it. For example, I have a large
number of aliases for the git commands I run most often, listed below. I
recommend that you dedicate an area of your .bashrc
to put aliases.
# Git aliases
alias gco='git checkout'
alias gcob='git checkout -b'
alias gc='git commit'
alias gcm='git commit -m'
alias gs='git status -s'
alias gl='git log'
alias gpl='git pull'
alias gps='git push'
alias gpssu='git push --set-upstream origin $(git rev-parse --abbrev-ref HEAD)'
Searching and finding
I strongly recommend that you familiarize yourself with grep
and find
. From
the man-page of grep
we learn that it “prints lines that match patterns” and
while it sounds simple, it’s incredibly useful. A very common usecase is to
search a log file for some information or to find lines in a man-page that
mentions a certain word. For example, to quickly check if there is an option to
see the size of a file with ls
you could run man ls | grep size
, which
pipes the output of man ls
to grep size
to only print the lines where size
is mentioned.
--block-size=SIZE
with -l, scale sizes by SIZE when printing them; e.g., '--block-size=M'; see SIZE format below
with -l and -s, print sizes like 1K 234M 2G etc.
-s, --size
print the allocated size of each file, in blocks
-S sort by file size, largest first
sort by WORD instead of name: none (-U), size (-S), time (-t), version (-v), extension (-X)
-T, --tabsize=COLS
find
, on the other hand, is used to “search for files in a directory
hierarchy”, according to the man-page. Sometimes you may find that you do not
know where a file is located, and that’s when you’d use find
. To search for a
file with a certain name you could use find . -name 'filename'
and find will
recursively go through all directories and print every file it finds with a name
that matches the given pattern. You could even pipe the result to fzf
if there
are many matches!
find
and grep
can sometimes be a bit slow and if you find yourself waiting
for these tools often, it might be worthwhile to install their modern
alternatives ripgrep
for grep
and fd
for find
.
Multi-tasking
I’ve mentioned tmux
in passing, but it has recently become one of my favourite
programs for productivity on the command line. Tmux stands for terminal
multiplexer and is a way to emulate several terminals within a single window.
You can start a session by running tmux
, which will give you a green bar at
the bottom of the screen, indicating that it is active. I use tmux constantly,
to the point where I feel naked without it. I’ve found that it is especially
useful when I have long-running tasks, like deep learning model training, that I
want to have in the background and not risk terminating prematurely. I can
simply detach from the session, and even if I close the terminal window I can be
confident it’s still going. It’s also really useful when you are working on
several projects at once, since you can easily jump between sessions to switch
context. You can even script tmux so you can start up or attach to your
development environment with a single command.
The keybindings for tmux are not the most intuitive, so when starting out I would recommend this cheat sheet. By default, all tmux keybindings are prefixed with CTRL-B and followed by another keypress. Here are my top 10 most useful tmux commands
%
- Split the window with a vertical line"
- Split the window with a horizontal linearrow-key
- Change focus to the pane in the arrow-key directionCTRL
+arrow-key
- Resize pane in arrow-key directionx
- Close focused paned
- Detach from session. It will still run in the background. List all sessions withtmux list-sessions
and attach to an existing session withtmux attach -t <target-session>
c
- Create a new window in the current session0
…9
- Select window by numbers
- Open session selector[
- Enter copy-mode. In copy mode:q
to quit,Spacebar
to start selection andEnter
to copy selection
If you think tmux might be useful for you I cannot recommend this amazing mini-course by Chris Toomey enough. Follow it and you wil end up with a really usable configuration.
The last tip I want to share is background processes. Let’s say you’ve started a
long-running process, like a web server, that has taken over your terminal
session. You can move that process to the background by pressing CTRL-Z and you
will get back control of the terminal. You can foreground the process again with
fg
. To start a command to be run in the background from the beginning, you can
add an &
to the end of the command. Please note, however, that this is
different from detaching from a tmux
session, since when you close your
terminal session with background tasks they will be terminated.
Conclusion
There are many more things wish to mention, but I wanted to keep this post at a
level where you should be able to follow along even with only a tiny amount of
command-line experience. If you find yourself in that group of people then I
hope that I’ve learned something new and that you maybe you might be willing to
give life on the command life a try. I assure you, it can even be quite
$HOME
-ey.
In the future I may write posts about more advanced topics, like alternative shells and advanced customizations. But until then, happy hacking.