Shell Basics¶
The shell reads a command line, expands it, and asks the kernel to run programs; the terminal only carries characters between the keyboard, the screen and the shell. Separating terminal, shell and console explains most "works in my SSH session, fails in cron" surprises.
Track: Core · Interview weight: Med
Must-Know Facts¶
| Fact | Value | Verify with |
|---|---|---|
| Terminal | Device that carries input and output (/dev/pts/N for SSH, /dev/ttyN for consoles) | tty |
| Shell | Program that parses and runs commands (bash, dash, zsh) | echo $0 |
| Login shell of a user | Field 7 of /etc/passwd | getent passwd $USER |
| Allowed login shells | /etc/shells | cat /etc/shells |
/bin/sh | bash on RHEL, dash on Ubuntu | readlink -f /bin/sh |
| Virtual consoles | getty@ttyN services; Ctrl+Alt+F2 switches on physical machines | systemctl list-units 'getty@*' |
| Interactive shell | $- contains i | echo $- |
| Login shell | Started by login, sshd or bash -l; reads profile files | shopt login_shell |
| History file | ~/.bash_history, written when the shell exits | echo $HISTFILE |
| Trace a command line | set -x prints each command after expansion | bash -x script.sh |
Terminal, Shell and Console¶
| Term | What it is | Example |
|---|---|---|
| Terminal emulator | Graphical program that draws a terminal | GNOME Terminal, iTerm2 |
| Pseudo-terminal (pty) | Kernel device pair used by SSH and terminal emulators | /dev/pts/0 |
| Virtual console | Text terminal on the machine's own screen | /dev/tty1 |
| Serial console | Terminal over a serial line; used by VMs and cloud consoles | /dev/ttyS0 |
| Shell | Command interpreter running inside a terminal | bash |
script -qc 'tty; ps -o pid,tty,comm -p $$' /dev/null </dev/null
ps -o pid,tty,comm -p $$
systemctl list-units --no-legend 'getty@*' 'serial-getty@*'
Output:
/dev/pts/0
PID TT COMMAND
3036 pts/0 ps
PID TT COMMAND
3030 ? bash
getty@tty1.service loaded active running Getty on tty1
serial-getty@ttyS0.service loaded active running Serial Getty on ttyS0
script allocates a pseudo-terminal, so tty reports /dev/pts/0. The same shell run by an automation agent has no terminal (?), which is also the situation inside cron jobs and systemd services.
Which Shell Is Running¶
echo "$SHELL"
cat /etc/shells
ls -l /bin/sh
Output:
/bin/bash
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
lrwxrwxrwx 1 root root 4 Oct 29 2024 /bin/sh -> bash
echo "$SHELL"
cat /etc/shells
ls -l /bin/sh
Output:
/bin/bash
# /etc/shells: valid login shells
/bin/sh
/usr/bin/sh
/bin/bash
/usr/bin/bash
/bin/rbash
/usr/bin/rbash
/usr/bin/dash
lrwxrwxrwx 1 root root 4 Mar 31 2024 /bin/sh -> dash
A #!/bin/sh script is not a bash script on Ubuntu
dash rejects bash features such as [[ ]], arrays and source. A script that works when run as bash script.sh can fail as ./script.sh or from cron; use #!/bin/bash when the script needs bash.
The failure can be silent. This script starts with #!/bin/sh and tests [[ $name == web* ]]:
./check.sh
echo "rc=$?"
bash check.sh
Output:
./check.sh: 3: [[: not found
rc=0
matched
Under dash, the failed test makes the if false, and the script still exits 0.
$SHELL holds the login shell from /etc/passwd, not the shell that is currently running; echo $0 and ps -p $$ show the current one.
Interactive and Login Shells¶
$- lists the shell's active options; i marks an interactive shell. The login_shell option shows whether profile files were read.
bash -c 'echo $0 $-'
bash -ic 'echo $0 $-'
bash -lc 'shopt -q login_shell && echo login'
Output:
bash hBc
bash hiBHc
login
| Shell started by | Interactive | Login |
|---|---|---|
| SSH session, console login | Yes | Yes |
| New terminal tab on a desktop | Yes | No (most emulators) |
su -, sudo -i | Yes | Yes |
bash script.sh, cron, systemd | No | No |
Startup files per shell type are covered in Variables and Environment.
Command Anatomy¶
A command line is split into words: the first word is the command, words starting with - are options, and the rest are arguments. Short options can be combined (ls -la); long options use two dashes (--all); -- ends option parsing.
echo 'ls -l /etc/hostname' | bash -x
Output:
+ ls -l /etc/hostname
-rwxr-xr-x 1 root root 9 Sep 16 13:10 /etc/hostname
The + line is the command after expansion, which is the fastest way to see what a script really ran.
History and Line Editing¶
set -o history -o histexpand
HISTTIMEFORMAT='%F %T '
ls -d /etc/ssh
echo !$
history 2
Output:
/etc/ssh
echo /etc/ssh
/etc/ssh
3 2026-09-16 13:35:51 echo /etc/ssh
4 2026-09-16 13:35:51 history 2
Interactive shells enable history expansion by default; the set line is needed only in scripts.
| Keys or syntax | Action |
|---|---|
Ctrl+R | Search history backwards |
!! / !$ / !n | Previous command / its last argument / history entry n |
Alt+. | Insert the last argument of the previous command |
Ctrl+A / Ctrl+E | Start / end of line |
Ctrl+W / Ctrl+U / Ctrl+K | Delete word before cursor / to start / to end |
Ctrl+L | Clear the screen |
Tab, Tab Tab | Complete; list completions (bash-completion adds options) |
Ctrl+C / Ctrl+D / Ctrl+Z | Interrupt / end of input / suspend |
Keep secrets out of history
With HISTCONTROL=ignorespace (included in Ubuntu's ignoreboth), a command that starts with a space is not saved. Passing tokens as arguments still exposes them in ps output, so prefer files or environment variables.
Common Errors¶
./check.sh: 3: [[: not found¶
Cause: the script runs under dash (/bin/sh on Ubuntu) and uses bash syntax; the format <script>: <line>: <command>: not found is dash's.
Fix: set the shebang to #!/bin/bash or run it with bash script.sh.
not a tty¶
Cause: tty (exit code 1) or a prompting program runs without a terminal: cron, systemd, CI jobs, ssh host cmd.
Fix: remove interactive prompts from automation, or force a pseudo-terminal with ssh -t when a prompt is required.
Interview Checkpoints¶
L1: What is the difference between a terminal, a shell and a console?
Say first: the terminal carries text in and out, the shell interprets the commands, and the console is the machine's own terminal (screen or serial line).
Proof: tty shows /dev/pts/0 over SSH; ps -p $$ shows the shell.
Follow-up: What changes for a program started by cron, which has no terminal?
L1: What is the difference between a login shell and an interactive shell?
Say first: a login shell is the first shell of a session and reads profile files; an interactive shell reads commands from a user; SSH gives both, a script gets neither.
Proof: echo $- contains i; shopt login_shell reports on after bash -l.
Follow-up: Which startup files does each type read?
L2: Find which shell you are running and which shell the account is configured with.
Say first: the running shell and the configured login shell can differ.
Proof:
ps -o comm= -p $$
getent passwd "$USER" | cut -d: -f7
Follow-up: Why is $SHELL not reliable for the first question?
L2: Show exactly what a command line expands to before it runs.
Say first: turn on execution tracing.
Proof: set -x; ls $HOME/*.conf; set +x
Follow-up: How do you trace a whole script without editing it? (bash -x script.sh.)
L2: Re-run the previous command with sudo without retyping it.
Say first: history expansion.
Proof: sudo !!
Follow-up: How do you find a command you ran last week? (Ctrl+R or history | grep.)
L3: A script works when run by hand but fails with syntax errors from cron.
Say first: compare the interpreter and the environment, starting with the shebang.
Proof: head -1 script.sh shows #!/bin/sh; readlink -f /bin/sh shows dash; the failing line uses [[.
Follow-up: Which other environment differences does cron introduce? (PATH, no terminal, no profile files.)
L4: What does the shell do when a command is typed and Enter is pressed?
Say first: it reads the line, splits it into tokens, performs expansions, sets up redirections, resolves the command, then forks a child that calls execve and waits for it.
Proof: strace -f -e trace=clone,execve,wait4 bash -c 'ls /tmp'
Don't say: "The terminal runs the command."
Related¶
- Command Resolution: how the shell finds the program
- Quoting and Expansion: what happens to the words before execution
- Variables and Environment: startup files per shell type
- Architecture: fork, exec and system calls
Captured on Rocky Linux 10.2 and Ubuntu 24.04.4 LTS (iximiuz Labs microVMs, kernel 6.1.167), 2026-09.