Introduction
I previously wrote an article about shell scripting, and to my surprise, it was read by far more people than I expected.
Since shell scripting isn’t exactly a “trendy” technology, I assumed there wouldn’t be much interest.
That experience made me realize:
There are probably more people who want to learn shell scripting than we think.
So in this article, I’d like to share a digest of essential shell scripting basics.
This guide is designed to cover “the minimum you need to know to avoid getting stuck when writing your first shell script.”
Rather than going deep into each topic, I focus on breadth and practical awareness.
This article is especially useful if you:
✅ Have never written a shell script before
✅ Tried once but struggled
✅ Want to understand how shell scripting differs from other languages
Basic Rules of Shell Scripts
The .sh Extension Is a Convention
Technically, shell scripts can have any file extension.
However, .sh is the widely accepted convention.
hello.sh
backup.sh
deploy.sh
Using .sh enables syntax highlighting in editors and on GitHub, so unless you have a specific reason not to, stick with it.
Write a Shebang (#!) on the First Line
A shell script should start with a shebang:
#!/bin/bash
The shebang specifies which shell should execute the script.
It starts with #! followed by the full path to the shell.
One Command Per Line
In shell scripts, a newline separates commands, so the basic rule is one command per line.
If you want to split a command across multiple lines, use \:
echo "This is \
one command"
Comments Use #
Comments start with #. You can also place comments mid-line.
There is no native way to comment out multiple lines at once.
# This is a comment
echo "hello" # inline comments are allowed
Working with Variables
Defining Variables
Variables are created automatically using:
variable_name=value
No declaration is required.
Variable naming rules:
- Allowed characters: letters, numbers, and
_ - Cannot start with a number
- Lower snake_case is commonly used
- Variables are treated as strings by default
If you want an integer variable, you need to declare it explicitly:
# String variable
sum=2+9
echo $sum
# -> 2+9
# Integer variable
declare -i sum
sum=2+9
echo $sum
# -> 11
Referencing Variables
To reference a variable, prefix it with $:
name="Alice"
echo $name
# -> Alice
In bash, referencing an undefined variable does not cause an error—it expands to an empty string:
echo $name
# -> (empty output)
To clearly separate variable names from surrounding text, use {}:
item="pen"
echo "I have many ${item}s"
Single vs Double Quotes
Quotes behave differently depending on the type:
-
": variables are expanded -
': variables are not expanded
name="Alice"
echo "$name"
# -> Alice
echo '$name'
# -> $name
To escape characters inside double quotes, use \:
echo "\$name"
# -> $name
Common Control Structures
Conditional Branching (if / case)
if Statements
if [ condition ]; then
do_something
elif [ another_condition ]; then
do_something_else
else
fallback
fi
There are two condition styles ([] and [[]]).
If you’re curious about the difference, see this article:
https://dev.to/a-k-0047/whats-the-difference-between-and-in-shell-scripts-3kkj
case Statements
case is useful when handling multiple patterns:
case "$var" in
start)
echo "start"
;;
stop)
echo "stop"
;;
*)
echo "other"
;;
esac
Loops (for / while)
for Loop
for value in a b c; do
echo $value
done
while Loop
declare -i count=0
while [ $count -lt 5 ]; do
echo $count
count=$count+1
done
An infinite loop can be written like this:
while true; do
echo "looping..."
sleep 1
done
Functions
Functions are defined as follows:
hello() {
echo "Hello $1 and $2"
}
Arguments are accessed using $1, $2, $3, and so on.
Calling a function:
hello "Alice" "Bob"
# -> Hello Alice and Bob
Shell-Specific Concepts
Exit Status
On Linux, every command returns an exit status.
- 0 → success
- Non-zero → failure
The exit status is not displayed automatically.
You can check it using $?:
ls /usr
# -> bin libexec sbin standalone X11R6 lib local share X11
echo $?
# -> 0
ls /aaa
# -> ls: /aaa: No such file or directory
echo $?
# -> 1
When using exit status in if statements:
- 0 is treated as true
- Non-zero is treated as false
if ls /usr; then
echo "success"
else
echo "failure"
fi
# -> success
if ls /aaa; then
echo "success"
else
echo "failure"
fi
# -> failure
Command Substitution
You can store command output in a variable using $(...):
now=$(date)
echo $now
# -> Fri Dec 12 19:47:14 JST 2025
Pathname Expansion (Globbing)
Shells can expand patterns like * and ? into multiple filenames.
Examples:
ls
# -> file1.txt file2.txt string.c string.h string.txt
# ? matches a single character
ls string.?
# -> string.c string.h
# * matches any string
ls *.txt
# -> file1.txt file2.txt string.txt
# [] matches one of the listed characters
ls string.[ch]
# -> string.c string.h
Running Shell Scripts Manually
There are two main ways to execute a shell script.
Method 1: Execute Directly (Recommended)
./hello.sh
- Specify the script path (relative or absolute)
- Executed using the shebang-defined shell
- Requires execute permission:
chmod +x hello.sh - Script name alone is not enough
✅ ./hello.sh
❌ hello.sh
Method 2: Pass to the Shell
bash hello.sh
- Execute permission not required
- You must manually choose the correct shell
Conclusion
This article summarized the minimum knowledge needed to avoid getting stuck when writing your first shell script.
There are many more important patterns and techniques to learn, so I plan to write follow-up articles that dive deeper into specific topics.
💬 How about you?
- What was the most confusing part of shell scripting when you first learned it?
- Are there any shell scripting rules or behaviors that surprised you?
- Would you like to see a deeper article on a specific topic (e.g.
ifconditions, quoting, or debugging)?
Top comments (0)