I’ve opened plenty of old scripts — some my own, some written by other people — and struggled to understand what a particular line was doing six months after it was written. Good comments are the difference between a script you can maintain and one you’re afraid to touch. In this guide, I’ll show you exactly how commenting works in Bash and, just as importantly, how to comment well.
What Are Comments and Why They Matter
A comment is a piece of text in your script that Bash completely ignores when running the script. It exists purely for humans — to explain what a section of code does, why a decision was made, or to temporarily disable a line without deleting it.
Comments matter because:
- They help you (and others) understand the script months or years later
- They document assumptions, edge cases, and reasoning that aren’t obvious from the code itself
- They make debugging faster since you can quickly disable (“comment out”) problematic lines
The Basic Comment Syntax: #
In Bash, any line — or part of a line — starting with # is treated as a comment.
Example 1: Full-Line Comment
#!/bin/bash
# This script greets the user by name
echo "Hello, World!"
Output:
Hello, World!
The line starting with # is completely ignored by Bash; only the echo command actually runs.
Example 2: Inline Comments
You can also add a comment at the end of a line of actual code.
#!/bin/bash
count=10 # Initialize the counter to 10
echo "Count is: $count"
Output:
Count is: 10
Everything after the # on that line — including Initialize the counter to 10 — is ignored by Bash.
The Shebang Line Is Not a Comment (Technically)
You’ll notice every script in this article starts with #!/bin/bash. This looks like a comment because it starts with #, but it actually serves a special purpose — it’s called a shebang, and it tells the operating system which interpreter should run the script.
#!/bin/bash
If you remove this line, running ./script.sh directly might fail or use the wrong shell, depending on your system’s default. Bash itself does treat the shebang line as a comment once it starts executing, but the operating system reads it before Bash even starts, to determine which program should interpret the file.
Commenting Out Multiple Lines
Bash doesn’t have a true native multi-line comment syntax like /* */ in C, but there are a few common workarounds.
Method 1: Adding # to Every Line
#!/bin/bash
# echo "This line is disabled"
# echo "So is this one"
echo "Only this line will run"
Output:
Only this line will run
This is the clearest and most explicit method, though tedious for large blocks — most text editors let you comment out multiple selected lines at once with a keyboard shortcut, which makes this painless in practice.
Method 2: Using a Here-Document as a Block Comment
#!/bin/bash
echo "Before the comment block"
: << 'END_COMMENT'
This entire section is ignored.
You can write as many lines as you want here.
It won't be executed by Bash.
END_COMMENT
echo "After the comment block"
Output:
Before the comment block
After the comment block
Here, : is a no-op command (it does nothing but succeed), and << 'END_COMMENT' redirects a block of text into it as a here-document, which is simply discarded. This is a common trick for “commenting out” large chunks of code temporarily during debugging.
Method 3: Using if false; then ... fi
#!/bin/bash
echo "Before the block"
if false
then
echo "This will never run"
echo "Neither will this"
fi
echo "After the block"
Output:
Before the block
After the block
This isn’t a “true” comment — Bash still parses these lines syntactically — but since the condition is always false, none of the code inside ever executes. I use this less often than the here-document method, since it still requires the code inside to be syntactically valid Bash.
Writing Meaningful Comments
Simply knowing the syntax isn’t enough — comments should add value. Here’s how I approach it:
Bad Example
#!/bin/bash
x=5 # set x to 5
x=$((x + 1)) # add 1 to x
echo $x # print x
These comments just restate what the code already says — they add no real information.
Good Example
#!/bin/bash
# Starting inventory count before today's shipment arrives
stock=5
# Add the newly delivered item to inventory
stock=$((stock + 1))
echo "Current stock level: $stock"
Output:
Current stock level: 6
Now the comments explain the why, not just the what — which is far more useful when you or someone else revisits this script later.
Documenting a Script Header
For any script longer than a few lines, I like to include a header comment block describing the script’s purpose, usage, author, and version.
#!/bin/bash
#############################################
# Script Name: backup.sh
# Description: Backs up the /data directory
# to a compressed archive.
# Usage: ./backup.sh [destination_folder]
# Author: Jane Doe
# Version: 1.2
#############################################
destination=${1:-/backups}
tar -czf "$destination/backup_$(date +%F).tar.gz" /data
echo "Backup complete. Saved to $destination"
This kind of header immediately tells anyone opening the file what it does and how to use it, without them needing to read the whole script line by line.
Commenting Function Definitions
#!/bin/bash
# Calculates the square of a given number
# Arguments:
# $1 - the number to square
# Returns:
# Prints the squared value
square() {
local num=$1
echo $((num * num))
}
result=$(square 6)
echo "The square is: $result"
Output:
The square is: 36
Documenting a function’s expected arguments and return behavior is especially valuable once your scripts grow beyond a handful of lines.
How This Works Internally
- Bash’s parser scans each line character by character. The moment it encounters an unquoted
#, it treats the rest of that line as a comment and skips straight to the next line without evaluating anything after it. - Inside quotes (
"# not a comment"or'# not a comment'),#loses its special meaning and is treated as a literal character — this is whyecho "Price: #5"prints the#normally. - The shebang (
#!) is technically a comment as far as Bash’s own parser is concerned, but the kernel (via theexecvesystem call) inspects the first two bytes of an executable script file specifically for#!before handing control to Bash, which is how it knows to launch/bin/bashto interpret the rest of the file. - The here-document trick (
: << 'END') works because:is a builtin that ignores all its arguments and always returns success; redirecting text into it via a here-document means that text is read as input but never actually used for anything.
Real-World Use Cases
- Documenting complex regex or logic that isn’t self-explanatory at a glance.
- Temporarily disabling debug code without deleting it, using
#or a block-comment trick. - Adding TODO or FIXME markers for future work:
# TODO: Add error handling for missing config file
# FIXME: This breaks if the input contains special characters
- Version and changelog tracking directly inside a script header for quick reference.
Best Practices
- Comment the why, not the what — the code itself already shows what it does.
- Keep comments up to date; an outdated comment can be worse than no comment at all, since it misleads whoever reads it next.
- Use a consistent header format across all your scripts so team members know what to expect.
- Avoid over-commenting obvious code (e.g.,
# increment iabove((i++))) — it adds noise rather than clarity.
Security Considerations
- Never leave sensitive information (passwords, API keys, internal server names) in comments — comments are still visible to anyone who opens the file, and version control history can preserve them even after they’re removed.
- Be cautious with commented-out code that references credentials; delete it entirely rather than leaving it disabled but present.
Optimization Tips
- Comments have effectively zero runtime cost — Bash simply skips over them during parsing, so there’s no performance reason to minimize them. Comment generously where it adds clarity.
- For very long block comments, prefer the here-document method (
: << 'END') over commenting hundreds of lines individually — it’s easier to toggle on and off during debugging.
Troubleshooting Common Issues
- A
#inside a string isn’t being treated as a comment — that’s expected behavior;#only starts a comment when it appears outside of quotes. - Here-document block comment isn’t working — make sure the closing delimiter (
END_COMMENTin the examples above) matches exactly, with no trailing spaces, and appears at the start of its own line. - Script fails to run with “bad interpreter” error — check that the shebang line is the very first line of the file, with no blank line or whitespace before it.
Frequently Asked Questions
Q: Does Bash have real multi-line comments like /* */ in other languages? A: Not natively. The common workaround is a here-document redirected into the : no-op command, or commenting each line individually with #.
Q: Do comments slow down script execution? A: No, comments are skipped during parsing and have no measurable impact on performance.
Q: Is the shebang line (#!/bin/bash) a comment? A: Functionally, Bash treats it as a comment once it’s running, but the operating system reads it beforehand to determine which interpreter to launch — so it serves a real purpose beyond documentation.
Q: Should I comment every single line? A: No. Over-commenting obvious code adds clutter. Focus comments on non-obvious logic, assumptions, and the reasoning behind decisions.
Common Mistakes to Avoid
- Writing comments that just repeat the code without adding context.
- Letting comments go stale after the code changes, creating misleading documentation.
- Leaving sensitive data in comments (passwords, tokens, internal URLs).
- Forgetting the shebang line entirely, which can cause the script to run under the wrong shell.
Summary
Comments in Bash are simple to write — just prefix a line (or part of one) with # — but writing genuinely useful comments takes a bit more thought. Focus on explaining the reasoning behind your code, document script headers and functions clearly, and use the here-document trick when you need to disable large blocks temporarily. Good commenting habits pay off every time you, or someone else, has to revisit a script months down the line.
