Shells and the command line

Before we start, it’s important to note the difference between a shell and a terminal. Think of a terminal as a hardware device connected to a machine and a shell as the program running on that machine interpretting the commands. For information about terminals in detail: http://www.linusakesson.net/programming/tty/

A Shell is Just a Program

It’s easy to confuse a shell with the operating system, but the shell is really just a program like any other. Keep in mind that every process in a unix environment has been fork()ed from another process. An init process runs as pid 1 and all other processes descrend from it. You can see this using the pstree command.

So what does a shell do? It sits there, waiting for user input, and lets us control programs in an interative fashion.

It is even fairly straightforward to implement your own shell.

Job Control

Job control is an important aspect of any shell. In our modern world filled with enumurable virtual terminals, it might feel like job control commands are superfluous, but this is not the case (especially when dealing with remote systems).

Let’s use this simple python program…

import time

i = 0
while True:
    print("sleeping... {}".format(i))
    time.sleep(5)
    i += 1

To show how we can suspend jobs, inspect jobs, run them in the background, bring them back to the foreground, and disown them.

Builtins vs Programs

One common bit of confusion is the idea of a shell builtin vs an external command. One trick to test out whether or not something is a builtin in your shell is to use the which command. For example, on my linux system which ls prints out /bin/ls while which cd prints out nothing. This tells us that cd is interpretted directly by the shell and does not result in it forking off a new process. It is a builtin.

Consult this page of the bourne shell manual for a complete list of builtins in bash.

Environment Variables

Much like command line flags environment variables are a way to configure and influence how a program runs. For example if we want to use emacs to edit our crontab:

EDITOR=emacs contab -l

By default, variables in bash are ‘local’ to the currently running shell and not passed down to any subprocesses. The export built-in changes that behavior:

export EDITOR=emacs
crontab -l

Pipelines & Redirection

pipelines are an incredibly handy feature implemented in almost all unix shells.

Here we use cat to output the contents of a file and pipe that through grep.

cat /proc/meminfo | grep MemTotal

Redirection is another capability of almost all shells. It’s easy to redirect the output of some command to a file:

ls > ls_out

Redircting stderr is also easy:

ls /thisfiledoesnotexist 2> ls_errs

The variety of shells in the world is mindblowing. We’ll take a look at a few big ones.

Bash

bash is the shell you are probably most familiar with. These days it is the de facto standard on linux desktops, servers, and mac os machines.

c shell

c shell was invented to more closely match c-style syntax in shell programming. Though it seems to fall more out of use day by day, tcsh was the default shell in early versions of mac os X.

zsh & fish shell

zsh and fish shell offers some modern conveniences both in terms of interactivity and programming. Both are probably most lauded for their ability to add autocomplete to programs (like git, etc), making life easier on the command line.

ash

ash is a slimmed down version of bash found on many embedded devices and android devices, too.

Shell Programming

In addition to working interactively, most shells are also their own programming langauge, allowing us to write scripts that to all sorts of things. Languages like bash have the things we might expect from fully featured languages like conditionals, loops, and functions.

Bash can be tricky to get your head around and so something like fish shell tries to emulate the appearance of modern languages.