When working with shell scripts (bash/sh), you might need to check if a string variable begins with specific characters. A common use case is identifying comment lines that start with the hash symbol (#). The intuitive approach using [ myvar = #* ]
doesn't work because:
# This won't work:
if [ myvar = #* ]; then
echo "Starts with #"
fi
Method 1: Using Parameter Expansion
The most efficient way in bash:
myvar="#comment asfasfasdf"
if [[ "$myvar" == \#* ]]; then
echo "Variable starts with #"
fi
Method 2: POSIX-compliant Solution
For maximum portability across different shells:
myvar="#comment asfasfasdf"
case "$myvar" in
\#*) echo "Starts with #" ;;
*) echo "Doesn't start with #" ;;
esac
Method 3: Using grep
Alternative approach using pattern matching:
if echo "$myvar" | grep -q '^#'; then
echo "Found hash prefix"
fi
- Forgetting to quote variables (
"$myvar"
vs$myvar
) - Using
[ ]
instead of[[ ]]
in bash - Not escaping the # character properly
The parameter expansion method (Method 1) is fastest as it doesn't spawn subshells or external processes. The case statement (Method 2) is nearly as fast and more portable.
Here's a practical script that processes lines in a file:
#!/bin/sh
process_file() {
while IFS= read -r line; do
case "$line" in
\#*)
echo "Skipping comment: $line"
;;
*)
echo "Processing: $line"
# Your logic here
;;
esac
done < "$1"
}
process_file "input.txt"
Many developers coming from other programming languages often stumble when trying to perform pattern matching in shell scripts. The example below shows a common mistake:
#!bin/sh
myvar="#comment asfasfasdf"
if [ myvar = #* ]
This approach fails because the [ ]
test construct doesn't support pattern matching in the same way that other programming languages do.
Here are several reliable methods to check if a shell variable starts with a hash character:
Method 1: Using Parameter Expansion
if [[ "${myvar}" =~ ^# ]]; then
echo "Variable starts with #"
fi
This uses bash's regex operator =~
. Note that this requires bash, not plain sh.
Method 2: POSIX-compliant Approach
case "$myvar" in
"#"*) echo "Found leading hash" ;;
*) echo "No hash" ;;
esac
This works in all POSIX-compliant shells and is the most portable solution.
Method 3: Using String Slicing
if [ "${myvar:0:1}" = "#" ]; then
echo "Starts with hash"
fi
This checks the first character directly. Note that some older shells might not support this syntax.
When testing variables that might be empty, always quote them:
myvar=""
if [ "${myvar:0:1}" = "#" ]; then
echo "This won't trigger"
else
echo "Handle empty case"
fi
Also consider variables that might contain only whitespace before the hash:
myvar=" #comment"
if [[ "${myvar#"${myvar%%[![:space:]]*}"}" =~ ^# ]]; then
echo "Starts with hash after trimming whitespace"
fi
For scripts processing many lines or running frequently, the case
statement tends to be fastest in benchmarks. The regex approach is more flexible but slightly slower.