How do I test if a variable is a number in Bash?

To test if a variable is a number in Bash, you can use regular expressions, arithmetic evaluation, or external tools like grep or awk. Below are detailed methods with examples, covering integers, floating-point numbers, and edge cases.

1. Using Regular Expressions (Most Flexible)

Check if the variable matches a numeric pattern using Bash’s [[ =~ ]] operator.

Example 1: Check for Integers

var="12345"
if [[ $var =~ ^-?[0-9]+$ ]]; then
    echo "$var is an integer"
else
    echo "$var is NOT an integer"
fi

Example 2: Check for Integers or Floats

var="-3.14"
if [[ $var =~ ^-?[0-9]+([.][0-9]+)?$ ]]; then
    echo "$var is a number"
else
    echo "$var is NOT a number"
fi

Example 3: Handle Leading/Trailing Spaces

Trim spaces before checking:

var="  42  "
trimmed_var=$(echo "$var" | xargs)  # Remove whitespace
if [[ $trimmed_var =~ ^-?[0-9]+([.][0-9]+)?$ ]]; then
    echo "$trimmed_var is a number"
fi

2. Arithmetic Evaluation (Integers Only)

Use arithmetic expansion (( ... )) to test if the variable is an integer.
Limitation: Fails for floating-point numbers.

var="123"
if (( var + 0 )) 2>/dev/null; then
    echo "$var is an integer"
else
    echo "$var is NOT an integer"
fi

Edge Case Handling

var="abc"
if (( var + 0 )) 2>/dev/null; then
    echo "Valid integer"
else
    echo "Invalid integer"  # Triggers for non-integers
fi

3. Using case Statements

Pattern-match numeric values with case:

var="123.45"
case $var in
    '' | *[!-.0-9]*) echo "$var is NOT a number" ;;
    *)               echo "$var is a number" ;;
esac

Improved case for Floats

var="-123.45"
case $var in
    -*) num_part="${var#-}" ;;  # Handle negatives
    *)  num_part="$var" ;;
esac

case $num_part in
    '' | .* | *.*.* | *[!0-9.]*) echo "$var is invalid" ;;
    *)                            echo "$var is valid" ;;
esac

4. Using External Tools (e.g., grep or awk)

Check with grep:

var="42"
if echo "$var" | grep -Eq '^-?[0-9]+$'; then
    echo "$var is an integer"
fi

Check for Floats with grep:

var="3.14"
if echo "$var" | grep -Eq '^-?[0-9]+(\.[0-9]+)?$'; then
    echo "$var is a float"
fi

Check with awk:

var="123"
if awk -v var="$var" 'BEGIN {exit !(var ~ /^-?[0-9]+$/)}'; then
    echo "$var is an integer"
fi

Handling Edge Cases

CaseExampleSolution
Leading zeros00123Use regex ^-?[0-9]+$ (allowed).
Scientific notation1.2e3Extend regex to ^-?[0-9]+(\.[0-9]+)?([eE][+-]?[0-9]+)?$.
Empty/undefined varvar=""Check [[ -z "$var" ]] before numeric tests.
Commas as decimals3,14Adjust regex: ^-?[0-9]+([,.][0-9]+)?$ (locale-specific).
Leading/trailing charsabc123Ensure regex anchors (^ and $) to reject non-numeric characters.

Full Example: Validate Integer or Float

is_number() {
    local var="$1"
    # Check for integer or float (including negatives and decimals)
    [[ $var =~ ^-?[0-9]+([.][0-9]+)?$ ]] || [[ $var =~ ^-?[.][0-9]+$ ]]
}

var="-123.45"
if is_number "$var"; then
    echo "$var is valid"
else
    echo "$var is invalid"
fi

Key Notes

  • Regex Anchors: Use ^ (start) and $ (end) to ensure the entire string is numeric.
  • Quotes: Always quote variables (e.g., "$var") to handle spaces and special characters.
  • Floats: Use [.][0-9]+ to validate decimals (e.g., 3.14, .5, 123. is invalid).
  • Performance: Regex ([[ =~ ]]) is faster than external tools like grep.

Choose the method that best fits your use case!

Leave a Reply

Your email address will not be published. Required fields are marked *