This page is a summary of some things you should know when working with servers via their shell.
There are many exceptions to this, but graphical user interfaces require a significant install code base and many servers chose not to install any of that.
There are a few exceptions to this: some companies that chose to depend entirely on one proprietary operating system provider and some chose to use less-common open systems like Irix or FreeBSD, but Linux has the lions share of the server market.
Shells are the programs you interact with when working on the command line. They expect you to provide strings to them, most of which run a process. You commonly interact with the shell through a GUI program called a terminal emulator.
The most common way to get access to a server shell is:
Open a terminal emulator on your computer: cmd.exe on Windows, Terminal.app on MacOS, the terminal window in VS Code, or any other terminal emulator you prefer. It will initially connect to some local shell program on your computer.
Start the ssh
process, which connects to the server and opens a shell there.
Send commands to the server’s shell.
Most Linux shells1 This page only discusses things common to all shells that are compatible with the POSIX standard definition of sh
. You’ll probably end up in a much fancier shell like bash
or zsh
with a wide range of extra features, but those extra features are beyond the scope of this page. are Turing-complete programming languages in their own right. They support variables, functions, conditionals, and loops. They typically have only one data type: string; but some also support arrays.
Variables are accessed by preceding them with $
, like $var
. Variables as assigned with =
, which must not have spaces around it, like var="new value"
.
Shells are optimized for running other programs. If you do any significant work in a shell script, you’re not using it as intended.
If I just run a process, like ls
, it’s stdout and stderr display to my terminal emulator and its stdin reads from my terminal emulator, which in turn will read from my keyboard.
If I want any one of these to interact with a file instead I can do that with just a few characters:
ls > /tmp/dump
redirects stdout to the file /tmp/dump
ls 2> /tmp/dump
redirects stderr to the file /tmp/dump
ls < /tmp/dump
redirects stdin to read from the file /tmp/dump
A more powerful tool is to have one process’s stdout map to another process’s stdin. This is also a single-character addendum:
ls | shuf
redirects ls
’s stdout to shuf
’s stdinThis stdin-to-stdout chaining is called piping
and the sequence of programs so chained is called a pipeline
. Many powerful shell operations involve piping.
It is unusual to pipe stderr. When building a tool for shell use, stdout should be used to data another application might find helpful and stderr for informing the user of errors or unexpected behavior.
If I enclose something in backticks like `ls /tmp`
or in dollar-parentheses like $(ls /tmp)
, it will be treated like a command to run and it will be replaced by its stdout.
mkdir
creates a directory for each of its arguments. seq
prints a sequence of integers to its stdout. Thus,
mkdir $(seq 340 343)
is the same as
mkdir 340 341 342 343
and creates four directories: $HOME/340
, $HOME/341
, $HOME/342
, and $HOME/343
.
There are some global-variable-like values called environment variables. These are even more global than most global variables because they persist when invoking a new application. There are many of these, but four are of particular interest:
$PWD
is the present working directory. Any relative paths are resolved relative to this directory. cd
is a shell command whose sole purpose is to change the value of PWD
.
$PATH
is a colon-separated list of paths to look for applications in. When I type ls
, it runs the first /some/path/ls
it finds where /some/path
is selected from $PATH
(often /usr/bin/ls
).
$USER
is the currently-logged-in user. This is just informative: the real log-in information is stored as a numerical ID in the kernel, but knowing who that is is often useful.
$HOME
is the home directory of the currently-logged-in user. ~
is shorthand for $HOME
. cd
with no arguments is equivalent to cd "$HOME"
.
a
vs 'a'
vs "a"
Any test you type is treated like a string…
… unless it has spaces, in which case it’s treated as several strings …
… or it has various other meaning-changing characters like $
, (
, and !
.
If you put it in double quotes, spaces don’t make several strings but other characters still have special meaning.
If you put it in single quotes, nothing has special meaning.
Assume I’m logged in as user student1
.
i am $USER
is three strings: i
, am
, and student1
."i am $USER"
is one string: i am student1
.'i am $USER'
is one string: i am $USER
.In addition to a few commands built into the shell itself, like cd
, any executable on the $PATH
can be used as a command in the shell.
When I log in to a new nothing-installed course VM and run
ls $(echo $PATH | grep -o '[^:]*') | wc -l
it tells me there are around2 This is only approximate because a few of the things it is counting might not be programs 2892 such programs which the shell will understand as commands. I used four of them in that line:
ls
shows me the file in a given directory.echo
sends its arugments to its stdout.grep
copies selected parts of its stdin to its stdout, using a regular expression to decide what to output. In this case the regular expression is [^:]*
which means any number of non-colons.
wc
counts how many lines, words, and characters are in its stdin; with -l
it only counts lines.The more commands you know, the more powerful the shell becomes.
man term
shows the manual page for term
; most applications and many library functions have manual pages.
man -k term
searches for manual pages that mention the term in their name or brief description.ls
shows the contents of the current directory.
ls -l
shows more info about each file.ls -A
shows hiddenfiles too.
cd
changes the value of PWD
.cp
copies files.
cp -r
copies directories with all their contents recursively.mv
moves or renames files or directories.rm
removes files.
rm -r
removes files and directories with all of the directories’ contents.mkdir
makes directories.echo
copies its arguments to its stdout; mostly used for assembling pipelines.cat
dumps the contents of its arguments to stdout.less
shows the contents of its argument file, or its stdin if none, one screen at a time with page-up, page-down, and search capabilities.
less
, press h
for its internal help including how to search and how to exit less
.nano
is a more-or-less self-documenting text editor.grep
searches for things
grep -n term filename
searches for term
in filename
and shows each line where it occursgrep -rl term directory
searches all files in directory
recursively for ones that contain term
somewhere inside and lists the files it findsThere are many, many others that are useful, but with just these you can do quite a lot.
There are various games designed to teach you about the shell. Three that I recommend are:
http://web.mit.edu/mprat/Public/web/Terminus/Web/main.html is a somewhat cheesy introduction to the basics of the command line.
Play this entirely in the browser.
http://overthewire.org/wargames/ has more coverage of ssh and related topics.
Play this using ssh
from your own terminal; to get started, try running ssh bandit0@bandit.labs.overthewire.org -p 2220
.
https://github.com/entrity/Spell-Quest has more coverage of tools I tend to use when programming.
Play this by cloning the repo and working in your own computer’s terminal.