Skip to main content

Linux concept: shell

21 July 2026

Every time you open a terminal, a single program is waiting there to catch what you type. You write ls, press Enter, and something has to read that word, find the matching program, run it, show you the result, and then ask you for the next command. That something is the shell. It is the program you talk to all day on a Linux server, the layer wrapped around the core of the system, and it is far more than a place to type commands: it is a full programming language, a command interpreter, and the glue that ties every small Unix tool into one working whole. Understanding the shell is understanding how you actually drive a Linux machine.

1. The Basics

A shell is a program that reads the commands you type, works out what you mean, and asks the rest of the system to carry them out. It sits between you and the kernel. You never speak to the kernel directly; you speak to the shell, and the shell arranges for programs to run on your behalf. When people say they are "on the command line" or "in a terminal", the program they are really talking to is a shell.

The clearest way to picture it is a simple loop that never stops while you work. The shell prints a prompt, waits for you to type a line, reads that line, breaks it into words, decides what to do, runs it, shows the output, and then prints the prompt again. This is often called the read-eval loop, and it is the whole heartbeat of interactive Unix:

print a prompt   ->   read a line   ->   understand it   ->   run it   ->   show output
      ^                                                                          |
      +--------------------------------------------------------------------------+

Two different jobs hide inside that loop, and it helps to keep them apart. As an interactive command interpreter, the shell is what you type into by hand. As a scripting language, the exact same program reads commands from a file instead, so a list of commands becomes a program you can run again and again. The remarkable thing about Unix shells is that these are not two tools: they are one program doing the same work with a different source of input.

The right mental model: the shell is an ordinary program whose whole job is to run other programs. It is not part of the kernel, it is not "the system" - it is a translator that turns the lines you type into requests the kernel can act on.

This article is about that translator: what it does, where the name comes from, how today's shells grew out of forty years of Unix history, how expansion and quoting really work, the difference between a builtin and a real program, and the surprising truth that the shell, not the command you called, does most of the clever work on your line. The examples are verified against GNU Bash 5.2 on Linux.

1.1 Seeing Which Shell You Run

Your account has a login shell, the shell that starts when you log in. The environment variable SHELL records it, and on most Linux systems it is Bash:

$ echo $SHELL
/bin/bash

That variable tells you your configured shell, but not necessarily the one running this very second. To ask the shell that is actually reading your commands right now, look at $0, the name of the running program:

$ echo $0
bash

The difference matters more than it looks. SHELL is set once at login and never changes; $0 reflects the shell you are in at this moment, which can be different if you started another shell by hand. Keep both in mind: one is your setting, the other is your reality.

1.2 The Shell Is a File Like Any Other

A shell is not magic and not built into the kernel. It is a normal executable program sitting on disk, and you can list it just like any other file:

$ ls -lh /bin/bash
-rwxr-xr-x 1 root root 1.4M ... /bin/bash

The list of shells your system considers valid login shells lives in a plain text file, /etc/shells:

$ cat /etc/shells
# /etc/shells: valid login shells
/bin/sh
/usr/bin/sh
/bin/bash
/usr/bin/bash
/usr/bin/dash

Each line is one program that can serve as somebody's shell. That is the first clue to something we return to at the end: the shell is replaceable. Nothing forces you to use Bash, and swapping it for another program in that list changes the whole feel of your command line without touching the system underneath.

1.3 The Terminal Is Not the Shell

Two words get mixed up constantly, and keeping them apart clears up a lot of confusion. The terminal is the window: an application that draws text on your screen and captures your keystrokes. The shell is the program running inside that window, reading those keystrokes and running your commands. The terminal is the glass; the shell is the voice behind it.

LayerWhat it isExamples
Terminal (emulator) The application that shows text and sends your keys to the shell GNOME Terminal, Konsole, Alacritty, Kitty
Shell The program inside it that interprets and runs your commands bash, zsh, dash, fish

The proof that they are separate is that you can mix and match them freely. The same Bash runs happily inside GNOME Terminal, Konsole, or Alacritty, and any of those terminals will just as happily run Zsh instead. The clearest case is a remote login: when you connect over SSH, there is no terminal emulator on the far machine at all. Your local terminal draws the text, but the shell you are typing into runs on the server, with nothing but a network connection between them. The shell does not own a window; it only reads input and writes output, and something else - a terminal, or SSH - carries those to your screen.

Back to top

2. Where the Name Comes From

The word shell is the second half of a matched pair, and the pair only makes sense together. The core of a Unix system, the part that talks to the hardware, is called the kernel, an English word for the soft seed at the centre of a nut. Wrapped around that core is the hard outer layer you actually touch: the shell. The names come straight from the picture of a nut, the kernel inside and the shell outside.

        you type here
             |
       [ SHELL ]        the outer layer you touch: bash, zsh, sh
       [ kernel ]       the core inside: talks to the hardware
             |
         hardware

So the name is a description of where the program sits. You live and work in the shell, the outer layer. Underneath, doing the real work with the hardware, sits the kernel. Every command you type in the shell eventually becomes a request that crosses inward to the kernel and a result that comes back out. The shell is, quite literally, the surface of the system.

The name is old. The very first program of this kind, written for the earliest Unix around 1971, was already called the shell, and its executable was named sh, short for "shell". That two-letter name has been carried forward for more than fifty years: on your system today, sh still names a shell, even though the program behind it has been replaced several times over.

Back to top

3. A Short History

The shells you use today are the current step in a long line that goes back to the beginning of Unix. Knowing the line explains why there are several shells, why they are almost but not quite compatible, and why the standard one is called what it is.

The first Unix shell was written by Ken Thompson at Bell Labs around 1971. It could run commands and redirect their input and output, but it was a small tool with almost no programming features. The real turning point came in 1979, when Stephen Bourne, also at Bell Labs, released a far more capable shell with Version 7 Unix. It had variables, loops, conditionals, and functions - a genuine programming language - and it kept the name sh. This Bourne shell became the template every later shell measured itself against.

Around the same time, Bill Joy at Berkeley wrote the C shell (csh) for BSD Unix, with a syntax closer to the C language and the first job control and command history. In 1983 David Korn at Bell Labs combined the best of both worlds in the Korn shell (ksh): the Bourne language plus the interactive features of the C shell.

Then came the shell most Linux users meet first. In 1989 Brian Fox wrote Bash for the GNU project, as a free replacement for the Bourne shell. Its name is a pun that tells you exactly what it is:

Bash stands for "Bourne Again SHell" - a free rewrite that is "born again" from the Bourne shell it replaces. The joke is also a promise: it speaks the Bourne language, so old scripts keep working, while adding decades of new interactive comfort on top.

Because Bash was free software and shipped with GNU, it became the default shell on almost every Linux distribution, which is why it is the one this article uses. The line did not stop there. Zsh, written by Paul Falstad in 1990, pushed interactive features furthest and is now the default on macOS. And dash, a small, fast, strictly minimal shell, is used on Debian and Ubuntu as the plain sh, because scripts that only need the standard language run quicker in it.

YearShellMilestone
1971 Thompson shell The first Unix shell; runs commands and redirects, little more
1979 Bourne shell (sh) A real programming language; the template for all that follow
1978 C shell (csh) C-like syntax, plus history and job control from Berkeley
1983 Korn shell (ksh) Bourne language married to C-shell interactive features
1989 Bash GNU's free "Bourne Again" shell; the Linux default
1990 Zsh The most feature-rich interactive shell; today's macOS default

One standard ties this family together. POSIX defines a common shell language, based on the Bourne and Korn shells, that every serious shell can run. Write a script to that standard and it works in Bash, dash, ksh, and zsh alike. That shared core is why a script written twenty years ago still runs, and why sh is a meaningful name no matter which program is behind it.

Back to top

4. Simple Use Cases

You already use the shell every time you type a command. But a few basic things are worth doing on purpose, because they show what the shell is doing rather than the command you happen to be running.

4.1 Running a Command and Passing Arguments

The simplest possible use of a shell is to type one word and press Enter. The shell splits your line into words separated by spaces: the first word is the command, and the rest are its arguments.

$ ls -l /etc
# word 1: ls        the command to run
# word 2: -l        an option, changing how ls behaves
# word 3: /etc      an argument, telling ls what to list

This splitting is the shell's work, not the command's. By the time ls starts, the shell has already cut the line into pieces and handed them over as a tidy list. That single habit - split the line, run the first word, pass the rest - is the foundation everything else builds on.

4.2 Starting Another Shell, and Leaving It

Because a shell is just a program, you can start one from inside another. Type its name and you are now in a new shell; type exit (or press Ctrl-D) and you drop back to the one you came from:

$ bash          # start a fresh Bash inside your current shell
$ echo $0
bash
$ exit           # leave it and return to where you were

This is how you try a different shell without changing your account. Type dash and you are in a strict, minimal shell; type exit and you are back in Bash. Nothing about your login has changed - you simply ran a program and then left it.

4.3 Asking the Shell What a Word Means

Before the shell runs a word, it decides what kind of thing that word is. You can ask it the same question with the type command (a shell builtin), which tells you whether a name is a program on disk, a shell builtin, an alias, or a function:

$ type ls
ls is /bin/ls
$ type cd
cd is a shell builtin
$ type echo
echo is a shell builtin

This is more useful than it first appears. It explains why cd behaves differently from ls, why two commands with the same name can act differently in two shells, and where a command actually comes from. When something on the command line surprises you, type is the first question to ask.

Back to top

5. Moderate Use Cases

Once you see the shell as an interpreter, the next step is to watch it rewrite your line before any command runs. Between the moment you press Enter and the moment a program starts, the shell performs a series of transformations called expansions. Most of the shell's power, and most of its surprises, live here.

5.1 Expansion: the Shell Rewrites Your Line

When you type a line, the shell does not hand it to the command as-is. It first expands several kinds of special text into their real values. The main ones are:

You typeThe shell turns it intoName
*.txt every filename ending in .txt filename expansion (globbing)
$HOME the value of the variable, e.g. /home/peter variable expansion
~ your home directory tilde expansion
$(date) the output of running date command substitution
{a,b,c} a b c brace expansion

Here is the key point most people miss: the command never sees the thing you typed. It sees the result. When you run ls *.txt, the ls program is never told about the *. The shell expands *.txt into the actual list of matching files first, and ls receives that list as ordinary arguments:

$ ls *.txt
# the shell first turns *.txt into: notes.txt report.txt
# then runs:  ls notes.txt report.txt

This one fact explains a great deal of Unix behaviour, and we come back to it as the biggest surprise in section 7.

5.2 Quoting: Turning Off Expansion

Because the shell treats characters like *, $, and spaces as special, you need a way to tell it "leave this text alone". That is what quoting does. Single quotes switch off all expansion; double quotes switch off most of it but still expand variables:

$ echo $HOME
/home/peter
$ echo "$HOME"        # double quotes: variable still expands
/home/peter
$ echo '$HOME'        # single quotes: taken literally
$HOME

Quoting is not decoration; it is how you control the shell. The classic bug is a filename with a space in it: without quotes the shell splits it into two arguments. Wrap a variable in double quotes - "$file" - and the whole name stays together. This is the single most common source of broken shell scripts.

5.3 Pipes and Redirection: Connecting Programs

The shell's other great power is plumbing. Every program starts with three streams already open, and the shell knows them by number as well as by name:

NumberNameDefault
0 standard input (stdin) your keyboard
1 standard output (stdout) your screen
2 standard error (stderr) your screen

The shell lets you redirect any of these streams to a file, or connect one program's output to the next program's input with a pipe, written |:

$ ls -l > listing.txt        # send output into a file (> means "write to")
$ ls -l >> listing.txt       # append instead of overwrite
$ sort < names.txt           # take input from a file (< means "read from")
$ ps aux | grep apache       # feed ps output straight into grep

The two output streams are separate on purpose, so normal results and error messages can go to different places. You steer stderr by its number, 2, and you can merge it into stdout with the special form 2>&1, meaning "send stream 2 to wherever stream 1 is going":

$ command 2> errors.txt       # send only error messages to a file
$ command > out.txt 2>&1      # send output AND errors to the same file
$ command 2> /dev/null        # throw error messages away

This is the idea that makes Unix what it is. Each tool does one small job and reads and writes plain text, and the shell snaps them together into a pipeline. The programs do not know they are connected; the shell arranges the streams so that the output of one becomes the input of the next. Almost every serious command line you write is a small pipeline the shell assembles for you.

5.4 Builtins Versus External Programs

When you type a word, the shell may run it in one of two ways. Most commands, like ls or grep, are separate programs the shell finds on disk and launches. But some commands are builtins: code that lives inside the shell itself and runs without starting any new program. Bash has about sixty of them:

$ type -a echo        # echo exists as both a builtin and a program
echo is a shell builtin
echo is /usr/bin/echo
$ type cd
cd is a shell builtin

The distinction is not just trivia. Some commands have to be builtins, because they change the shell itself - and we will see in section 7 exactly why cd is the classic example. For now, remember that a builtin is faster (no new program is started) and can do things an outside program cannot.

Back to top

6. Advanced Use Cases

The sections above showed the shell running one line at a time. Here we look at how it starts, how it keeps its settings, how it runs whole files as programs, and how it manages several jobs at once. This is where casual use turns into real command of the system.

6.1 Login, Non-Login, Interactive, Non-Interactive

A shell can start in different modes, and the mode decides which configuration files it reads. Two questions define the mode. Is it a login shell (started when you log in) or not? Is it interactive (typing at a prompt) or not (running a script)?

How it startedModeReads (Bash)
You log in over SSH or on the console login, interactive /etc/profile, then ~/.bash_profile
You open a new terminal tab in a desktop non-login, interactive ~/.bashrc
You run a script file non-login, non-interactive usually none

This table explains a headache that catches everyone eventually. You add a setting to ~/.bashrc, log in over SSH, and it is ignored - because a login shell reads ~/.bash_profile, not ~/.bashrc. The usual fix is to have ~/.bash_profile load ~/.bashrc, so both kinds of shell end up with the same setup. When a customisation "does not stick", the mode of your shell is almost always why.

6.2 Shell Variables Versus Environment Variables

The shell holds two kinds of variables, and the difference decides whether the programs you launch can see them. A plain shell variable exists only inside the current shell. An environment variable, created by export, is copied into every program the shell starts:

$ name=peter                 # a shell variable, private to this shell
$ echo $name
peter
$ bash -c 'echo $name'       # a child shell does NOT see it
                             # (prints an empty line)

$ export name                # now promote it to the environment
$ bash -c 'echo $name'       # the child shell sees it
peter

This is exactly why PATH, HOME, and LANG are exported: every program needs them, so they live in the environment, not just in one shell. It also explains a common confusion - a variable you set in a script is gone the moment the script ends, because the script ran in its own shell and took its variables with it.

6.3 Exit Status: How Commands Report Success

Every command that finishes hands the shell a number, its exit status: 0 means success, anything else means some kind of failure. The shell keeps the last one in the special variable $?, and this number is how commands are chained together with && ("and then, if it worked") and || ("or else, if it failed"):

$ ls /etc > /dev/null
$ echo $?
0
$ ls /nope 2> /dev/null
$ echo $?
2
$ mkdir build && cd build     # only cd if mkdir succeeded

Exit status is the shell's idea of true and false, and it is the backbone of every script. An if statement in the shell does not test a value; it runs a command and looks at its exit status. Once you see that 0 means success, the logic of shell scripting falls into place.

6.4 The Shell as a Programming Language: Scripts

Put a list of shell commands in a file and you have a program. The first line, the shebang, tells the system which shell should run the file; make the file executable and you can run it by name:

$ cat backup.sh
#!/bin/bash
for f in *.txt; do
    cp "$f" "$f.bak"        # note the quotes: safe with spaces
done

$ chmod +x backup.sh        # make it executable
$ ./backup.sh               # run it like any other command

Nothing here is a separate "scripting mode". The same expansions, quoting rules, variables, and exit statuses you use by hand are the language the script is written in. That is the deep economy of the Unix shell: learn to use it interactively and you have already learned to program it. The shebang line simply picks which shell reads the file, which is why a careful script names #!/bin/bash or #!/bin/sh on purpose rather than trusting whatever shell happens to be running.

6.5 Job Control: Several Things at Once

An interactive shell can run more than one program at a time and let you switch between them. End a command with & to start it in the background, press Ctrl-Z to suspend the one in front, and use jobs, fg, and bg to manage them:

$ sleep 300 &            # start a job in the background
[1] 154860
$ jobs                   # list this shell's jobs
[1]+  Running    sleep 300 &
$ fg %1                  # bring job 1 back to the foreground

This is a feature of the shell, not the programs. The shell asks the kernel to keep a group of processes attached to your terminal and hands the terminal to whichever job you choose. It is why you can start a long copy, push it into the background, and keep working in the same window - the shell is quietly acting as a small task manager for your session.

6.6 Debugging a Script

When a script does the wrong thing, you do not have to guess. The shell can show you exactly what it runs and can be told to stop the moment something goes wrong. You turn these behaviours on with the set builtin, either on the command line or at the top of the script:

OptionShort forWhat it does
set -x trace (execute) print each command, fully expanded, just before it runs
set -e exit stop the script as soon as any command fails
set -u unset treat the use of an unset variable as an error
set -o pipefail - make a pipeline fail if any command in it fails, not just the last

The most useful of these for finding a bug is set -x, because it shows you the commands after expansion - the real values, not what you typed. That turns a silent wrong result into a visible one:

$ set -x
$ name=world
$ echo "hello $name"
+ name=world
+ echo 'hello world'
hello world
$ set +x            # turn tracing back off (+ instead of -)

The lines beginning with + are the shell's trace, showing that $name became world before echo ran. The three safety options together - written as one line set -euo pipefail at the top of a script - catch a huge share of real bugs by refusing to plough on after a failure or a typo in a variable name. And before you even run a script, the external tool shellcheck reads it and warns about the classic mistakes, above all unquoted variables:

$ shellcheck backup.sh
In backup.sh line 3:
    cp $f "$f.bak"
       ^-- SC2086: Double quote to prevent globbing and word splitting.

Running shellcheck on every script you write is the cheapest way to avoid the pitfalls this article keeps warning about. It knows the rules of the shell in detail and points at the exact line and character where you broke one.

Back to top

7. Something Most Users Do Not Know

7.1 The Shell Expands Your Line, Not the Command

This is the single most important thing to understand about a Unix shell, and most people learn it by accident. When you run rm *.log, the rm program never sees the *. The shell expands *.log into the actual list of matching filenames first, then hands that list to rm. The command has no idea a wildcard was ever involved. This explains behaviour that otherwise looks strange: why * matches nothing in an empty directory and is passed through literally, why quoting a pattern stops it matching, and why you can use wildcards with any command at all, even ones that know nothing about them. The glob is the shell's doing, done before the program starts.

7.2 cd Cannot Be a Real Program

The command that changes your current directory, cd, has to be a shell builtin - it could not possibly be a separate program. Here is why. Each running program has its own current directory, and a program cannot change its parent's directory. If cd were an external program, the shell would start it, that new program would change its own directory, then exit, and your shell would be exactly where it started. The change would vanish with the program that made it. The only way to move the shell's own directory is to run code inside the shell, which is precisely what a builtin is. This is the concrete reason some commands must live inside the shell instead of on disk.

7.3 sh Is Probably Not the Shell You Think

On Debian and Ubuntu, /bin/sh is not Bash. It is a symbolic link to dash, a smaller and stricter shell:

$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 ... /bin/sh -> dash

This matters the moment a script fails only when run as sh script. Bash quietly accepts many convenient extensions that plain sh does not, so a script with #!/bin/sh that uses a Bash-only feature works when you test it in Bash and breaks when the system runs it with dash. The lesson is to match the shebang to the language you actually wrote: use #!/bin/bash if you rely on Bash features, and #!/bin/sh only for strictly standard scripts.

7.4 The Shell Runs Almost Nothing Itself

An interactive shell is mostly a launcher. When you type ls, the shell does not list the directory; it finds the ls program, makes a copy of itself with the fork system call, replaces that copy with ls using exec, and waits for it to finish. The famous fork-and-exec pattern is the heart of how Unix starts programs, and the shell does it for nearly every command you run. The shell's own code handles the prompt, the expansions, the pipes, and the builtins - and then it steps aside and lets a separate program do the actual work.

7.5 You Can Replace Your Shell Entirely

Because a shell is just a program named in /etc/shells, you are free to change the one you get at login. The chsh command ("change shell") does exactly that, and from then on every terminal greets you with the shell you picked:

$ chsh -s /usr/bin/zsh        # make zsh your login shell
$ echo $SHELL
/usr/bin/zsh

This is why the shell is genuinely an outer layer and not a fixed part of the system. People switch from Bash to Zsh for its completion, or use fish for its friendliness, and nothing underneath cares: the kernel, the commands, and the files are all the same. Only the program that reads your commands has changed, which is the whole point of the shell being a shell.

Back to top

8. Best Practices

  • Quote your variables. Write "$file", not $file. Double quotes keep a value with spaces or special characters as one piece and prevent the single most common class of shell bugs.
  • Know which shell your script targets. Start scripts with a deliberate shebang: #!/bin/bash if you use Bash features, #!/bin/sh only for strictly standard code. Do not assume sh is Bash.
  • Use type when a command surprises you. It tells you whether a name is a builtin, a program, an alias, or a function, which explains most "why did that do something different?" moments.
  • Put interactive settings in ~/.bashrc, and load it from ~/.bash_profile. That way both login and non-login shells share the same aliases, prompt, and environment.
  • Export variables that programs need, keep the rest local. Use export for things like PATH that every program should see; leave temporary working values as plain shell variables.
  • Check exit status in scripts. Chain steps with &&, test $?, and stop early when a command fails rather than blindly running the next step on broken data.
  • Prefer clarity over cleverness. A shell one-liner can grow into an unreadable knot. When a pipeline gets long, a short, quoted, commented script is easier to trust than a dense single line.
  • Read the documentation. The shell is thoroughly documented. man bash is long but complete, help lists the builtins, and help cd or help type explains any one of them.
$ man bash        # the full Bash manual
$ help            # list all shell builtins
$ help type       # documentation for a single builtin
$ type -a NAME    # every meaning of a command name
Back to top

9. Common Mistakes

9.1 Myth versus Reality

MythReality
"The shell is part of the operating system core." The shell is an ordinary program. The kernel is the core; the shell is a replaceable layer on top of it.
"The command I run handles the * wildcard." The shell expands * into filenames before the command starts. The command only sees the finished list.
"sh is just another name for Bash." On Debian and Ubuntu, /bin/sh is dash. Bash-only features break when a script runs under sh.
"Single and double quotes are the same." Single quotes switch off all expansion; double quotes still expand variables. The difference is often the bug.
"cd is a program like ls." It must be a builtin. An external program could not change its parent shell's directory.
"A variable I set is visible to the programs I run." Only if you export it. A plain shell variable stays private to the shell that set it.
"My ~/.bashrc runs every time I log in." A login shell reads ~/.bash_profile. If that file does not load ~/.bashrc, your settings are skipped.

9.2 Other Traps to Avoid

  • Leaving variables unquoted. An unquoted $file with a space in it becomes two arguments. Quote it as "$file" unless you have a specific reason not to.
  • Assuming a script's environment survives. Variables set inside a script vanish when it ends, because the script ran in its own shell. To change your current shell, use source script instead of running it.
  • Trusting #!/bin/sh to mean Bash. Test scripts with the shell named in the shebang, not just interactively in Bash, or they may fail only in production.
  • Forgetting that expansion happens first. A dangerous rm * is dangerous precisely because the shell expands * into every file before rm runs. Check what a glob matches with echo * first.
  • Confusing $SHELL with the running shell. $SHELL is your configured login shell; $0 is the shell running right now. They differ whenever you start another shell by hand.
  • Editing the wrong startup file. Put a setting in ~/.bashrc but log in over SSH and see it ignored? That is the login-versus-non-login trap from section 6.1.
Back to top

10. Summary

The shell is the program you talk to on a Linux machine: the outer layer wrapped around the kernel, a command interpreter and a full programming language in one. It reads your line, expands it, connects programs into pipelines, and asks the kernel to run them. It is not the system and not the core - it is a replaceable translator that turns what you type into what the machine does. See it that way and the command line stops being a mystery and becomes a tool you can reason about.

  • The shell is an ordinary program that reads commands and runs other programs; the kernel is the core, and the shell is the layer you touch on top of it.
  • It runs a simple loop: print a prompt, read a line, expand and understand it, run it, show the output, repeat.
  • The same program is both an interactive interpreter and a scripting language; learn one and you have learned the other.
  • The name pairs with kernel from the image of a nut, and the two-letter name sh has named a shell since about 1971.
  • Today's shells descend from the 1979 Bourne shell; Bash ("Bourne Again SHell") is the Linux default, with Zsh and dash as common relatives, all tied together by the POSIX standard.
  • Before any command runs, the shell performs expansions - globbing, variables, tilde, command substitution - so the command sees the result, never what you typed.
  • Quoting controls expansion: single quotes switch it all off, double quotes still expand variables.
  • Pipes and redirection let the shell connect small tools into pipelines, which is the core idea of Unix.
  • Some commands are builtins living inside the shell; cd must be one, because only code inside the shell can change the shell's own directory.
  • Startup files depend on the login/non-login and interactive/non-interactive mode, which is why a setting sometimes seems to be ignored.
  • Exported variables reach the programs you launch; plain shell variables do not. Every command reports an exit status, where 0 means success.
  • The shell starts programs with the fork-and-exec pattern and can be swapped entirely with chsh, because it is just a program named in /etc/shells.

This is the quick reference worth keeping:

echo $SHELL              your configured login shell
echo $0                  the shell running right now
cat /etc/shells          the shells this system allows
type NAME                is a word a builtin, program, alias, or function
type -a NAME             every meaning of a command name
ls *.txt                 the shell expands *.txt before ls runs
"$var"                   quoted: safe with spaces and special characters
'text'                   single quotes: no expansion at all
cmd1 | cmd2              pipe: cmd1's output into cmd2's input
cmd > file               redirect output into a file (>> to append)
cmd < file               take input from a file
export NAME=value        make a variable visible to launched programs
echo $?                  the exit status of the last command
cmd1 && cmd2             run cmd2 only if cmd1 succeeded
sleep 300 &              run a command in the background
jobs / fg / bg           manage this shell's background jobs
source script            run a script in the current shell
chsh -s /usr/bin/zsh     change your login shell

A server whose shells and scripts are written with care - variables quoted, shebangs deliberate, startup files understood, exit status checked - is a server whose automation does what you expect and fails loudly instead of silently. If your scripts break in production but work when you test them, or a cron job behaves nothing like it does at your prompt, or nobody is quite sure which shell runs what, it pays to have the layer you type into set up and understood properly, because it is the surface through which every other part of the system is driven.

Back to top
Linux concept: shell
Peter Martin
Peter Martin
Joomla Specialist

Peter is a Joomla specialist and a Linux admin for fast, secure and scalable websites.