Shell Basics
Shell Invocation
Section titled “Shell Invocation”A shell is both an interactive command interpreter and a scripting language interpreter. When you open a terminal emulator, it spawns a shell process — bash``zshOr dash. When you run a script with ./script.shThe shebang line determines which interpreter processes The file.
Interactive vs Non-Interactive
Section titled “Interactive vs Non-Interactive”| Aspect | Interactive | Non-Interactive |
|---|---|---|
| Startup files | .bashrc``.profile``/etc/bash.bashrc | Only BASH_ENV variable if set |
| Prompt | PS1``PS2 displayed | No prompt |
| Job control | Enabled (set -m) | Disabled by default |
| Line editing | Readline active (readline library) | Not active |
| Aliases | Expanded | Expanded (in bash) or not (POSIX mode) |
| Exit on error | Does not exit on error | Same unless set -e |
The shell”s startup sequence differs depending on whether it is a login shell or a non-login Shell:
- Login shell: Sourced on first login (SSH,
su -``login). Reads/etc/profileThen~/.bash_profileor~/.bash_loginor~/.profile(first one found). - Non-login interactive shell: Reads
~/.bashrc. - Non-login non-interactive shell: Inherits exported environment variables. Does not read startup files unless
BASH_ENVpoints to one.
flowchart TD
A[Shell Invoked] --> B{Login Shell?}
B -->|Yes| C{Interactive?}
B -->|No| D{Interactive?}
C -->|Yes| E[/etc/profile]
E --> F[~/.bash_profile]
F --> G[~/.bashrc]
C -->|No| H[BASH_ENV]
D -->|Yes| I[~/.bashrc]
D -->|No| J{BASH_ENV set?}
J -->|Yes| K[Source BASH_ENV]
J -->|No| L[No startup files]In practice, most systems configure ~/.profile to source ~/.bashrcSo both login and non-login Interactive shells load the same configuration. However, scripts executed by cron or systemd do not Source ~/.bashrc — this is a frequent source of bugs.
POSIX Shell vs Bash
Section titled “POSIX Shell vs Bash”POSIX specifies a shell standard (IEEE 1003.1, also known as the Single UNIX Specification). Bash is Largely POSIX-compliant but adds extensions. When writing portable scripts, target POSIX sh. When Writing for known-bash environments, use bash features deliberately.
| Feature | POSIX sh | Bash |
|---|---|---|
| Arrays | No | Yes (arr=()) |
[[ ]] | No | Yes (preferred over [ ]) |
(( )) | No | Yes (arithmetic evaluation) |
| Process substitution | No | Yes (<()``>()) |
| Associative arrays | No | Yes (bash 4.0+) |
[[ $a =~ $re ]] | No | Yes (regex matching) |
${var:offset:length} | No | Yes (substring expansion) |
&\>\&2 redirection | No | Yes |
On Debian and Ubuntu, /bin/sh is dash — a minimal POSIX shell that is significantly faster than Bash but lacks bash extensions. Scripts that use bash features must use #!/bin/bashNot #!/bin/sh.
Command Structure
Section titled “Command Structure”Every command executed by the shell follows this general pattern:
command [options] [arguments] [--] [operands]The shell performs the following steps before executing a command:
- Tokenization: Split the input line into words based on
IFS(Internal Field Separator, default: space, tab, newline). - Alias expansion: Replace aliases (only in interactive shells, and not for the first word in certain contexts).
- Brace expansion: Expand
{a,b,c}patterns. - Tilde expansion: Replace
~with$HOME``~userwith user’s home directory. - Parameter expansion: Expand
$VAR``${VAR}``${VAR:-default}Etc. - Command substitution: Execute
$(cmd)or`Cmd`and replace with stdout. - Arithmetic expansion: Evaluate
$((expression)). - Word splitting: Split results on
IFS(except in contexts where it is suppressed). - Pathname expansion (globbing): Expand
*``?``[...]patterns. - Quote removal: Remove quoting characters that are not part of expansions.
- Redirection: Set up I/O redirections.
- Command execution: Execute the command using the resolved path.
Pipe buffer size: Linux pipes have a default buffer of 64 KiB (since kernel 2.6.11, configurable Via /proc/sys/fs/pipe-max-size). When the buffer is full, the writing process blocks until the Reader consumes data. For high-throughput pipelines, this can be a bottleneck.
Process Substitution (Bash)
Section titled “Process Substitution (Bash)”Process substitution creates a temporary named pipe (FIFO) or /dev/fd/ entry:
# Compare output of two commandsdiff <(command1) <(command2)
# Feed a command's output as a file argumentwc -l <(find /etc -name "*.conf")
# Tee output to multiple processescommand > >(process1) 2> >(process2)File Descriptor Manipulation
Section titled “File Descriptor Manipulation”# Open file descriptor 3 for readingexec 3< /path/to/file
# Read from fd 3read -r line <&3
# Open fd 4 for writingexec 4> /path/to/output
# Write to fd 4echo "data" >&4
# Close file descriptorsexec 3<&-exec 4>&-
# Duplicate stderr to stdout, keeping stderr on fd 2exec 2>&1This is particularly useful in scripts where you need to maintain open file handles across multiple Operations:
#!/bin/bashexec 3>&1 4>&2exec > >(tee /var/log/script.log) 2>&1
# All output now goes to both terminal and log fileecho "This goes everywhere"
# Restore original file descriptorsexec 1>&3 2>&4exec 3>&- 4>&-Globbing Patterns
Section titled “Globbing Patterns”The shell expands glob patterns into matching filenames before passing them to the command. This is Fundamentally different from regex — globbing operates on filenames in the filesystem, not on Arbitrary text.
| Pattern | Meaning | Example | Matches |
|---|---|---|---|
* | Any sequence of characters (except leading dot) | *.log | app.log``error.log |
? | Exactly one character | file?.txt | file1.txt``fileA.txt |
[abc] | One character from the set | [abc].sh | a.sh``b.sh``c.sh |
[a-z] | One character from the range | [a-m]* | apple``banana``middle |
[!abc] | One character NOT in the set | [!0-9]* | file``name |
** | Recursive glob (bash 4.0+, globstar option) | **/*.py | All .py files recursively |
# Enable recursive globbingshopt -s globstar
# Delete all .tmp files recursivelyrm -v **/*.tmp
# Match files starting with a dotls -la .*
# Case-insensitive matching (bash 4.0+)shopt -s nocaseglobls *.TXT # matches file.txt, FILE.TXT, etc.