Bash Scripting
Variables
name="Alice" # assign (NO spaces around =)
count=42
msg="Hello ${name}" # variable substitution with braces (preferred)
today=$(date +%F) # command substitution
echo $name # reference
echo "${name}" # preferred: braces prevent ambiguityRules:
- No spaces around
= - Names: start with letter, no whitespace/punctuation
- Reference:
$varor${var}(braces preferred)
Environment Variables
export MYVAR=Value # export to child processes
printenv # list all env vars
echo $SHELL # read specific varQuoting
| Quote | Behavior |
|---|---|
"double" | $var and \ interpreted; other special chars protected |
'single' | Everything literal — NO variable substitution |
\char | Escape single character |
var="hello world"
echo "$var" # → hello world (single argument, correct)
echo $var # → hello world (but word-splitting can cause bugs)
echo '$var' # → $var (literal, no substitution)Always quote variable references: "$var" (prevents word splitting and glob expansion).
Functions
# Definition must come BEFORE call
greet()
{
local name="$1" # local = function-scoped
local age="$2"
echo "Hello ${name}, age ${age}"
}
greet "Alice" 30 # call (no brackets)Positional Parameters
| Variable | Meaning |
|---|---|
$0 | Script name |
$1 – $9 | Arguments 1–9 |
$# | Number of arguments |
$? | Exit status of last command |
if [ $# -gt 0 ]; then
echo "Got $# arguments, first is $1"
fiExit Status
0= success, non-zero = failure$?= exit status of last command
ls /usr/bin; echo $? # → 0 (success)
ls /nodir; echo $? # → 2 (failure)
exit 0 # script success
exit 1 # script failureif / test / [ ]
if [ expression ]; then
commands
elif [ expression ]; then
commands
else
commands
fitest Expressions
File tests:
| Expression | True if |
|---|---|
-f file | file exists and is regular file |
-d file | file is a directory |
-e file | file exists (any type) |
-r file | file is readable |
-w file | file is writable |
-x file | file is executable |
f1 -nt f2 | f1 is newer than f2 |
String tests:
| Expression | True if |
|---|---|
-z string | string is empty |
-n string | string is not empty |
s1 = s2 | strings are equal |
s1 != s2 | strings are not equal |
if [ -f "$HOME/.bashrc" ]; then
echo "File exists"
fiShebangs
#!/usr/bin/env bash # portable — recommended
#!/bin/bash # hardcoded path
#!/usr/bin/env python # Python scriptsFirst line of any script. env finds the interpreter in PATH — more portable than hardcoding.
chmod — File Permissions
Numeric
Format: chmod <owner><group><world> file
| Value | Permission |
|---|---|
| 4 | read (r) |
| 2 | write (w) |
| 1 | execute (x) |
Add them: chmod 755 = owner(rwx=7) + group(r-x=5) + world(r-x=5)
| Mode | Meaning | Use for |
|---|---|---|
777 | rwxrwxrwx | Full access everyone |
755 | rwxr-xr-x | Scripts/dirs (execute for all) |
644 | rw-r—r— | Regular files |
700 | rwx------ | Owner only |
400 | r-------- | Read-only by owner |
Symbolic
chmod u+x script.sh # add execute for owner
chmod a+r file.txt # add read for all
chmod a-x file.txt # remove execute for all
chmod go+rw file.txt # add read+write for group+world
chmod -R 755 /path/dir/ # recursiveu=user/owner, g=group, o=others, a=all
chown
sudo chown kate file.txt # change owner
sudo chown :mygroup file.txt # change group
sudo chown kate:mygroup file.txt # change bothsource vs bash
| Command | Shell instance | Variables/functions persist? |
|---|---|---|
source file.sh (or . file.sh) | Current | Yes |
bash file.sh | New | No |
Use source for libraries; bash/./script.sh for standalone scripts.
source .venv/bin/activate # classic use case: activate venv in current shellSee Also
- Linux and Bash Commands topic
- bash-scripting source