How to trim whitespace from a Bash variable?
I have a shell script with this code:
var=`hg st -R "$path"`
if [ -n "$var" ]; then
echo $var
fi
But the conditional code always executes, because hg st always prints at least one newline character.
Is there a simple way to strip whitespace from $var (like trim() in PHP)?
Or is there a standard way of dealing with this issue?
I could use sed or AWK, but I’d like to think there is a more elegant solution to this problem.
To trim whitespace from a Bash variable, you can use several methods including parameter expansion, sed, awk, or read command. The most elegant solution for your hg st issue would be to use parameter expansion patterns or the read command to strip whitespace, then check if the resulting variable is empty.
Contents
- Understanding the Problem with
hg st - Using Parameter Expansion to Trim Whitespace
- Using
readCommand for Whitespace Removal - Using
sedfor Trimming - Using
trfor Simple Whitespace Removal - Checking if Variable is Empty After Trimming
- Complete Solution for Your
hg stIssue - Performance Comparison
- Handling Multi-line Variables
Understanding the Problem with hg st
The issue you’re experiencing occurs because the hg st command (Mercurial status) always outputs at least one newline character, even when there are no changes to report. This makes Bash treat the variable as non-empty, causing your conditional [ -n "$var" ] to always execute.
As noted in the research, this is a common problem when dealing with commands that output newlines or whitespace characters. Your original code:
var=`hg st -R "$path"`
if [ -n "$var" ]; then
echo $var
fi
Will always print the output because var contains at least a newline character, making it non-empty to Bash.
Using Parameter Expansion to Trim Whitespace
Bash provides powerful parameter expansion capabilities that can be used to trim whitespace without external commands. This is often the most elegant solution:
Basic Trimming Patterns
# Remove leading whitespace
var="${var#"${var%%[![:space:]]*}"}"
# Remove trailing whitespace
var="${var%"${var##*[![:space:]]}"}"
More Readable Version with extglob
shopt -s extglob
var="${var##+( )}" # Remove leading whitespace
var="${var%%+( )}" # Remove trailing whitespace
shopt -u extglob
Comprehensive Trimming Function
trim() {
local var="$1"
# Remove leading whitespace
var="${var#"${var%%[![:space:]]*}"}"
# Remove trailing whitespace
var="${var%"${var##*[![:space:]]}"}"
printf '%s' "$var"
}
This approach is very efficient as it uses only Bash built-in features without spawning external processes source.
Using read Command for Whitespace Removal
The read command can be used to strip all leading and trailing whitespace from a variable:
var=" hello world "
read -r var <<< "$var"
echo "='$var='" # Output: '=hello world='
For your specific hg st issue:
var=`hg st -R "$path"`
read -r var <<< "$var"
if [ -n "$var" ]; then
echo "$var"
fi
This works because read automatically strips leading and trailing whitespace characters (spaces and tabs) when reading into a variable. However, note that this method may not preserve internal whitespace exactly as-is source.
Using sed for Trimming
sed is a powerful tool for text manipulation and can be used to trim whitespace:
Single-line trimming
var=" test string "
var=$(sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' <<< "$var")
Function approach
trim_sed() {
local var="$1"
var=$(sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' <<< "$var")
printf '%s' "$var"
}
For multi-line variables, you can use GNU Sed’s -z option:
trim_multiline_sed() {
local var="$1"
var=$(printf '%s' "$var" | sed -z 's/^[[:space:]]*//' | sed -z 's/[[:space:]]*$//')
printf '%s' "$var"
}
The sed approach is versatile but may be slower than parameter expansion due to the external process overhead source.
Using tr for Simple Whitespace Removal
The tr command can be used for simple whitespace removal, though it’s more suited for removing specific characters rather than trimming:
# Remove all whitespace including newlines
var=$(echo -n "$var" | tr -d '[:space:]')
# Remove newlines only
var=$(echo -n "$var" | tr -d '\n')
For your hg st issue, you could simply remove the newline:
var=$(hg st -R "$path" | tr -d '\n')
if [ -n "$var" ]; then
echo "$var"
fi
This approach works well but removes all newlines, which might not be what you want if you need to preserve line breaks source.
Checking if Variable is Empty After Trimming
After trimming, you need to properly check if the variable is empty. Here are several approaches:
Using -z test
trimmed_var=$(trim "$var")
if [ -z "$trimmed_var" ]; then
echo "Variable is empty after trimming"
fi
Using pattern matching
# Check if variable contains only whitespace
if [[ $var =~ ^[[:space:]]*$ ]]; then
echo "Variable is empty or contains only whitespace"
fi
Using parameter expansion
if [ -n "${var// /[[:space:]]}" ]; then
echo "Variable contains non-whitespace characters"
fi
These methods help you accurately determine if your variable contains meaningful content after trimming source.
Complete Solution for Your hg st Issue
Here’s a complete solution using parameter expansion:
#!/bin/bash
path="/your/repo/path"
# Get the status and trim whitespace
var=$(hg st -R "$path")
var="${var#"${var%%[![:space:]]*}"}" # Remove leading whitespace
var="${var%"${var##*[![:space:]]}"}" # Remove trailing whitespace
# Check if variable is meaningful
if [ -n "$var" ]; then
echo "$var"
else
echo "No changes detected"
fi
Or using the read method:
#!/bin/bash
path="/your/repo/path"
# Get status and trim using read
read -r var <<< "$(hg st -R "$path")"
if [ -n "$var" ]; then
echo "$var"
else
echo "No changes detected"
fi
Both solutions will properly handle the case where hg st outputs only newlines source.
Performance Comparison
Different methods have different performance characteristics:
| Method | Speed | Portability | Features |
|---|---|---|---|
| Parameter expansion | Fastest | Bash only | Limited features |
read command |
Fast | POSIX | Good for basic trimming |
sed |
Medium | Universal | Powerful but slower |
tr |
Fast | Universal | Good for simple removal |
For shell scripts where performance matters, parameter expansion is generally the best choice. For portability across different shells, the read method is excellent source.
Handling Multi-line Variables
If your variable might contain multiple lines and you need to trim each line individually, you can use a loop:
trim_multiline() {
local var="$1"
local result=""
while IFS= read -r line; do
# Trim each line
line="${line#"${line%%[![:space:]]*}"}"
line="${line%"${line##*[![:space:]]}"}"
# Add non-empty lines to result
if [ -n "$line" ]; then
result+="$line"$'\n'
fi
done <<< "$var"
printf '%s' "$result"
}
This preserves the line structure while trimming whitespace from each line source.
Conclusion
Trimming whitespace in Bash can be accomplished through several methods, each with its own advantages:
- Parameter expansion is the most elegant and efficient method, using only Bash built-ins
readcommand provides a simple, portable solution for basic trimmingsedoffers powerful pattern matching for complex casestris excellent for simple character removal
For your specific hg st issue, either parameter expansion or the read method would be the most appropriate solution. Both will effectively remove the trailing newline that’s causing your conditional to always execute.
The key takeaway is that while Bash doesn’t have a built-in trim() function like PHP, the shell’s parameter expansion capabilities provide powerful tools for string manipulation that are both efficient and elegant.
Sources
- Stack Overflow - How to trim whitespace from a Bash variable
- Baeldung on Linux - How to Trim Whitespace From a Bash Variable
- nixCraft - Bash Shell Remove & Trim White Spaces From String / Variable
- Reddit r/bash - Remove leading and trailing spaces from a variable
- Unix & Linux Stack Exchange - How do I trim leading and trailing whitespace from each line?
- Greg’s Wiki - BashFAQ/067
- ioflood - How to Trim Whitespace In Bash Shell Scripts
- Goran et al. - Trimming a string with Bash