Terminals and Shells 1: Fundamentals
This is part of a series covering 'glue' knowledge. This is stuff that may be difficult to find in any normal training material. If you're a new developer or programmer you will hopefully find it useful. I try to explain more of the implementation side if it helps understanding.
This particular part of the series is about terminal and shell usage. I focus mostly on Linux based shells.
Fundamentals
Basic terminology
- Terminal: Historically this was the physical device you would type at and see output on. The physical device part isn't so true anymore, its now the interface to a command line.
- Shell: The program that actually receives input, interacts with the machine, and sends out output. Most often refers to a command line interface.
- Command line: A type of shell that accepts text and returns text. As opposed to a graphical shell with windows and a mouse cursor.
You use a terminal to access a shell. There are many shells (programs) such as Bash, Zsh, Fish. Typically they end with 'sh' for 'shell'. There are many terminals such as Windows Terminal, Command Prompt, iTerm, just 'Terminal'.
Terminals typically let you control the colours, font, font size, etc. Most
shells have special characters/codes that tell a terminal to display certain
colours. Usually these codes are for named colours like 'blue' or 'red', and the
terminal determines the exact colours they will take on, like #FF4444
.
- Executable: A file that can be executed to do something on the computer. On Linux, any file with the 'execute' bit set. Can be a text file with a 'hashbang', or binary/non-text. Running an executable is sometimes referred to 'calling' that executable. This will be explained more later in the series.
Paths and directories
In a shell, you have a current working directory, (often abbreviated cwd). This is the directory that you are 'in'. Any relative paths are typically relative to the cwd.
Paths are strings of characters representing where something may be in a
filesystem. Your home directory might have the path /users/bob
. Slashes are
used to separate the directory and file names that make up the path. /users
is
a directory, containing a bob
directory. To make the path we join them with a
slash.
Paths beginning with a slash /
are absolute. This means that they
unambiguously point somewhere in the filesystem. This is in contrast to
relative paths, such as src/test.js
. This path could point to any
test.js
in any src
directory, you could have many of these on your own
system.
You can't actually create or manipulate things with relative paths. They always need to be converted to absolute paths in order to determine the exact place to manipulate. This is normally done by the executable you're calling. To convert relative paths, you need an absolute path to 'join' it with. Often this will be your current working directory.
If you refer to src/test.js
when your cwd is /home/users/my-project
, most
programs will understand that path to refer to
/home/users/my-project/src/test.js
.
There are some 'special' parts of paths. If we call the parts between slashes segments, then there are three special segments:
- A single dot:
.
, meaning 'this directory'. - A double dot:
..
, meaning 'this directories parent directory'. - A tilde ('til-duh'):
~
, meaning your home directory.
If you are in my-project/src
, you might refer to the README with
../README.md
. You'll often find it useful to use .
when referring to files
relative to the current directory, like executing a local script with eg
./my-script.sh
.
Paths can have spaces and other whitespace in them. You have to take care to
quote paths if you're uncertain if they have spaces. This is more important
when writing scripts and programs and will be talked about more then. You can
quote paths using double ("/users/Bob Jones"
) or single ('/users/Bob Jones'
). Shells do treat these differently, which we will get to later.
ls
and argument conventions
ls
('el-ess') lists the 'directory entries' for the current directory.
'Directory entries' are things like files and directories. Linux generally
refers to all of these things as 'files'...
Everything is a file
-Things that are mostly true on Linux
But 'file' in common parlance refers to what Linux calls a 'regular' file. These contain data, sometimes human readable, sometimes not. Other types of files exist, like block devices, pipes, and sockets.
ls
will show you what's in your current working directory. But you can also
pass it an argument, like ls my-project
and it will instead list the
contents of the that directory (ls
will recognise it is a relative path, and
make it absolute using cwd). You can also pass absolute paths like ls /
and ls /home/users
.
Most executables like ls
or rm
take arguments. These are a series of
strings typed after the command name itself. Each argument is separated by
spaces:
ls one two three
ls
here is being called with three arguments, one
, two
and three
.
Executables can treat these arguments however they want, and it varies a lot.
There are many conventions regarding arguments.
If what you want to pass as an argument containing spaces, you need to quote them:
# (this is wrong) ls /users/Bob Jones
ls
will see this as two separate arguments due to the space, /users/Bob
and
Jones
. This isn't likely what you wanted, and you need to do ls "/users/Bob Jones"
to get it to print the contents of Bob's home directory.
You can pass other arguments with dashes like ls -l
to list more detail about
each entry. This -l
is called a 'short option' because it's a single character
after the dash. There are usually 'long option' forms for each short option.
ls
doesn't have this, but if you wanted to print the version of Python you
could do the short python3 -V
or long python3 --version
. Long options often use a
double-dash, but not always. They typically start with the same letter, but not
always.
Short options can often be grouped together, but not always. ls -h -l
will
show extra detail for each entry (-l
) but with human-readable file sizes
(-h
). This will show things like '3.1M' for 3.1 Megabytes, rather than eg
'3250588'. These options can be grouped together like ls -lh
. It knows they
are two options, not a single long option, because of the single dash. The order
typically doesn't matter eg ls -hl
is the same.
How relative paths, short options, and long options are treated is up to the
executable. ls
and most other common commands follow these conventions. But
not always!
man
and help
At this point you should be frustrated about the 'but not always'. Thankfully,
you can use the man
command to learn about other commands! man ls
will tell
you all about ls
. man cd
will tell you all about cd
... well actually it
won't, you'll get something explaining 'built-ins'. Ignore this.
man
does work for a lot of programs, though, so it's worth a shot. The pages
that appear are called 'manpages', short for 'manual pages'. This is what they
are: manuals. They're not written to explain the command, but to document.
Searching online will generally give better introductions.
Commands without manpages will often have a 'help' option, like -h
or
--help
or -help
. Sometimes running the command with no arguments will show
help. It's all a mess of inconsistency.
cd
You probably already know that you can change your current directory with cd
.
It's good to know you can do several directory levels at once: cd my-project/src/thing
. You can also 'go back' to the last directory you cd
'd
into using a single dash: cd -
You can use absolute paths, you can use ..
several times as well: cd ../../..
to go up three levels of directories.
Aside: cd
isn't really an executable. It's a 'built-in' of the shell. Changing
the current working directory isn't something an executable could do for you.
This will be more clear later when we talk about processes.
Tab tab tab
Any time you're typing a command or a path... hit tab! Tab will generally autocomplete the current command/path if its not ambiguous. If it is ambiguous mashing tab a few times will tell you the things that it could be. You only need to type enough to make it 'biguous'.
Tab. Hit it.
Aside: Some terminals will cycle through options if it is ambiguous, rather than show all potential choices.
More useful commands
Lets cover some commands you should definitely be aware of.
pwd
will print your cwd. You can think of it meaning 'print working
directory', or 'present working directory'. Figuring out why its a 'p' is a
rabbit hole of history.
cat
will print out files you specify to the terminal. 'cat' is a abbreviation
of 'concatenate'. This is because cat a.txt b.txt
will output the two files
one after the other, ie concatenating them. Outside of scripts and 'pipelines'
its usually for just showing the contents of a file.
head
and tail
are two commands that also show the contents of a file, but
only the first few lines for head
, or the last few lines for tail
. Useful if
a file is massive. Takes lots of arguments to control how much they print.
mkdir
will make a new directory. It takes one or more paths that specify where
to make them, so mkdir one two three
will make three directories in the
current directory. You can do mkdir one/blah
, but the one
directory needs to
already exist. Some shells support -p
that will make parents as needed, so
mkdir -p a/b/c/d/e/f/g
would make all of the needed directories to get to
making g
.
rmdir
is the opposite, it will remove a directory, if it is empty. If you
expect what you're deleting is empty, it's good to use this to not
accidentally delete a directory of stuff you did want.
rm
will delete the files at the paths given to it. rm -r
will
recursively delete, meaning if you delete a directory, it will delete
everything in it (by recursing into it). You often need to throw an -f
on
there, rm -rf
. You can't undo this. Use with caution.
echo
will take its arguments and print them out. The usefulness of this
becomes apparent when you learn the rest of the shell features.
Useful shortcuts
Many terminals will have keyboard shortcuts to do certain things, but mostly they will forward keystrokes to the shell. The shell itself has a bunch of keyboard shortcuts.
Some of these shortcuts work on most shells, these work for Bash and Zsh which are likely the ones you're using. There are many more; these are the ones I find useful and can actually remember consistently.
Ctrl-a
moves your cursor (where you're typing) to the start of the command
you're typing.
Ctrl-e
moves to the end of the command you're typing.
Ctrl-r
lets you search through the commands in your command history. You can
type any part of a previous command and find it. Pressing Ctrl-r
again will
jump further back in history to the next match. Ctrl-c
will get you out of it.
Ctrl-c
will interrupt the currently running command, normally causing it to
exit and let you type commands again. More on this later.