For DevelopersJune 17, 2025

Bash Shell Scripting: How to Check Variable Types

Bash treats all variables as strings but offers ways to check their effective types using declare, regex, and arithmetic tests. Here’s how to ensure your scripts behave as expected.

Unlike strongly-typed languages, Bash treats all variables internally as strings, but provides several reliable methods to determine their effective "types." 

You can use declare -p to inspect variable attributes (-i for integers, -a for arrays, -A for associative arrays), employ regex patterns with anchors ([[ $var =~ ^[0-9]+$ ]]) to validate numeric content, leverage arithmetic evaluation ((( var + 0 ))) which fails with non-numeric values, or check array properties with expansion techniques (${#array[@]}). 

Each approach serves different needs:

  • Attribute inspection reveals declared intentions
  • Regex testing confirms actual content patterns
  • Arithmetic context verifies computational compatibility

Understanding these techniques is crucial whether you're validating configuration settings, processing input data, or building dynamic data structures in your Bash scripts. 

While Bash variables are untyped by default, Bash does allow explicit type-like declarations via the declare (or typeset) builtin. As the Advanced Bash-Scripting Guide notes

 

"Bash does not segregate its variables by type - essentially, Bash variables are character strings."

 

This means even if you declare a variable with an integer attribute (e.g., declare -i b), it won't become strongly typed. Assigning a non-numeric value to an -i variable simply makes Bash treat it as 0. 

This article will guide you through effective techniques to check the type of variable in bash and help you write more reliable scripts.

Join Index.dev to work on complex, global engineering projects remotely!

 

 

Declaring Variables and Attributes

Bash's declare command can tag variables with attributes to influence their behavior. For example, declare -i num=10 marks num as an integer, declare -a arr=(…) marks an indexed array, and declare -A dict=(…) marks an associative array (hash). The special option -p prints a variable’s declaration (including any -i-a-A flags), which can be parsed to inspect its type. 

Understanding how to check type of variable in bash starts with understanding these attributes:

# Declare variables with different attributes
declare -i count=5        # declare integer attribute
declare -a fruits=(a b c) # declare indexed array
declare -A colors=([r]=red [g]=green)  # declare associative array

# Print variable declarations to inspect their types
declare -p count fruits colors

This prints something like:

declare -i count="5"
declare -a fruits=([0]="a" [1]="b" [2]="c")
declare -A colors=([r]="red" [g]="green")

From the output, we see -i for integer, -a for indexed array, -A for associative array, or no special flag for plain scalar variables. By inspecting these flags, we can determine the variable's "type". Explicitly declaring variables helps catch mistakes like trying to write to a read-only variable or treating a non-array as an array.

 

 

Checking for Numeric Values

To test if a variable is numeric, there are a few idioms:

Integer attribute (declare -i):

If you set declare -i x, any assignment to x is evaluated arithmetically. Non-integer strings become 0. For example:

# Using integer attribute to enforce numeric values
declare -i num=10
echo $num           # 10
num=abc
echo $num           # 0 (non-numeric "abc" was coerced to 0)

# Another approach - declare first, then assign
declare -i numeric_var
numeric_var="123"; echo $numeric_var  # 123
numeric_var="foo"; echo $numeric_var  # 0

When you declare a variable with -i, Bash automatically evaluates any assignment arithmetically. Non-numeric values are coerced to 0.

Regular-expression test:

This regex approach checks if the string consists entirely of digits. The ^ and $ anchors ensure that the entire string matches the pattern. For example:

# Using regex to check if a variable contains only digits
is_integer() {
    [[ $1 =~ ^[0-9]+$ ]]
    return $?
}

# Test cases
var1="12345"
if is_integer "$var1"; then
    echo "var1 is an integer"
else
    echo "var1 is NOT an integer"
fi
# Output: var1 is an integer

var2="12.3"
if is_integer "$var2"; then
    echo "var2 is an integer"
else
    echo "var2 is NOT an integer"
fi
# Output: var2 is NOT an integer

Arithmetic evaluation:

This approach leverages Bash's arithmetic context - non-numeric content causes the arithmetic expansion to fail, yielding a non-zero exit status. We redirect error messages to /dev/null to keep the output clean.

# Using arithmetic context to test for numeric values
is_numeric_arithmetic() {
    local val="$1"
    # Attempt arithmetic - will fail with non-numeric values
    (( val + 0 )) 2>/dev/null
    return $?
}

# Test cases
num1=42
if is_numeric_arithmetic "$num1"; then 
    echo "num1 is numeric"; 
else 
    echo "num1 is NOT numeric"; 
fi
# Output: num1 is numeric

num2=foo
if is_numeric_arithmetic "$num2"; then 
    echo "num2 is numeric"; 
else 
    echo "num2 is NOT numeric"; 
fi
# Output: num2 is NOT numeric

Also Read: 4 Easy Ways to Check Variable Types in Go

 

 

Regex Anchors and Symbol Breakdown

When using Bash's [[ ... =~ regex ]] test to check type of variable in bash, understanding regex anchors is crucial:

# Demonstrating the importance of regex anchors
demonstrate_anchors() {
    local var="123abc"
    
    # Without anchors - partial match is enough
    if [[ $var =~ [0-9]+ ]]; then
        echo "Partial match: digits found inside '$var'"
    else
        echo "No digits found in '$var'"
    fi
    
    # With anchors - entire string must match pattern
    if [[ $var =~ ^[0-9]+$ ]]; then
        echo "All digits: '$var' contains only digits"
    else
        echo "Not all digits: '$var' contains non-digit characters"
    fi
}

demonstrate_anchors

Output: 

Output: Partial match: digits found inside '123abc' Not all digits: '123abc' contains non-digit characters

The key regex elements for checking numeric values:

  • ^ (caret): Anchors the match at the beginning of the string
  • $ (dollar): Anchors the match at the end of the string
  • [0-9]: Matches any single digit (0 through 9). The POSIX equivalent is [[:digit:]].
  • +: Requires one or more occurrences of the preceding element (one or more digits)
  • -?: Optional minus sign (for negative numbers)

A robust pattern for integers would be ^-?[0-9]+$, which allows an optional leading minus sign followed by one or more digits.

Bash's regex is POSIX Extended Regular Expressions, so the Perl-style shortcut \d does not work here. For example, [[ "$var" =~ ^\d+$ ]] will fail, whereas [[ "$var" =~ ^[0-9]+$ ]] works.

For clarity:

  • A "good" numeric test uses anchors and a proper quantifier, e.g., [[ "$var" =~ ^[0-9]+$ ]].
  • A "bad" regex might omit anchors or misuse quantifiers, such as [[ "$var" =~ [0-9]+ ]] (matches partial strings) or using * in a way that allows empty strings (e.g., ^[0-9]*$ would incorrectly accept an empty variable).

 

 

Checking for Arrays and Lists

An indexed array in Bash is a variable created with declare -a (or simply arr=(...)). To detect if a variable is an array, you can again use declare -p: it will output declare -a name=... for indexed arrays. 

For example:

declare -a pets=(dog cat bird)
out=$(declare -p pets 2>/dev/null)
if [[ $out == declare\ -a* ]]; then
    echo "pets is an indexed array"
fi
# Output: pets is an indexed array

Another indirect check: an indexed array’s length can be queried with ${#arr[@]}. If you have arr=(x y), then ${#arr[@]} returns 2, whereas for a scalar $str="x" it returns 1. A multi-element length >1 implies an array.

 

 

Checking for Associative Arrays

An associative array (hash) is declared with declare -A. To check for this, again use declare -p: it will show declare -A name=.... Example:

declare -A scores=([alice]=90 [bob]=85)
out=$(declare -p scores 2>/dev/null)
if [[ $out == declare\ -A* ]]; then
    echo "scores is an associative array"
fi
# Output: scores is an associative array

Alternatively, you can try parameter expansions: ${!assoc[*]} lists all keys of an associative array (and is empty for a normal variable), which can be a hint.

 

 

Comparing Bash vs Zsh (Type Handling)

While Bash is dynamically untyped, Zsh provides richer built-in types. Zsh supports integer and float types in addition to strings, as well as arrays and hashes. For example, in Zsh one can write:

# Bash example (no real types)
#!/bin/bash
declare -i age=30
echo $age    # 30
age=foo
echo $age    # 0 (foo was non-numeric)

# Zsh example (has built-in types)
#!/bin/zsh
typeset -i age=30
float salary=1234.56
echo $age $salary   # 30 1234.56

In Zsh, arrays are 1-indexed by default (element 1, 2, …) unlike Bash's 0-indexing. Also, Zsh will automatically treat unmarked numeric assignments as integers unless you explicitly declare float. In contrast, Bash only recognizes numbers in context; in Bash, 123 is numeric only when used in arithmetic.

 

These differences mean that some Bash idioms (like arithmetic checking or 0-indexing) don't apply in Zsh, and vice versa. For instance, Zsh's typeset -p will show integer var=... or float var=... for typed variables, while Bash's declare -p shows only -i/-a/-A flags.

Additionally, authoritative Bash references and guides explain that Bash variables are untyped (strings) by default and that declare can assign attributes. Zsh documentation notes the additional integer/float types and 1-based array indexing, as summarized above. 

Also Check Out: How to Check the Type of Variable in Java?

 

 

Best Practices

  • Always validate inputs before arithmetic or element access to prevent unexpected behavior.

     
  • Use functions (is_integeris_array) to encapsulate type checks; promotes reuse.

     
is_integer() { [[ $1 =~ ^[0-9]+$ ]]; }
if is_integer "$var"; then …; fi

 

  • Redirect stderr (2>/dev/null) when inspecting declare -p to suppress “not found” errors.

     
  • Prefer strict regex anchors (^…$) to avoid partial matches.

     
  • Document attributes when declaring (declare -ideclare -adeclare -A) so that future maintainers understand variable intentions. Example: 
declare -i timeout=30    # integer: request timeout (seconds)

 

 

Use Cases/Applications

  • Configuration Parsers: Ensure numeric parameters (timeouts, ports) are valid before applying them.

     
  • Data Processing Pipelines: When reading CSV or tab-delimited files, validate numeric columns.

     
  • Dynamic Data Structures: Scripts that build lists (arrays) of files, hosts, or service names can test existence before looping.

     
  • Error Handling: Early exit with clear messages if type checks fail, improving script robustness.

 

 

Conclusion

By combining regex-based checks, attribute enforcement (declare -i), and declaration inspection (declare -p), we can check the type of variable bash and achieve reliable type validation. These techniques prevent subtle bugs and make scripts safer for production use.

 

For Developers: 

Take your Bash scripting skills to the next level by exploring advanced tutorials and resources. Join Index.dev to access exclusive content, job opportunities, and professional growth tools.

For Companies: 

Looking to hire shell scripting experts who ensure rock-solid automation? Index.dev connects you with vetted talent within 48 hours with a risk-free 30-day trial. Learn how our platform can speed up your hiring process and drive your projects to success.

Share

Pallavi PremkumarPallavi PremkumarTechnical Content Writer

Related Articles

For EmployersHow Specialized AI Is Transforming Traditional Industries
Artificial Intelligence
Artificial intelligence is changing how traditional industries work. Companies are no longer relying only on general skills. Instead, they are using AI tools and specialized experts to improve productivity, reduce costs, and make better decisions.
Ali MojaharAli MojaharSEO Specialist
For EmployersHow to Scale an Engineering Team After Series A Funding
Tech HiringInsights
Most Series A founders hire too fast, in the wrong order, and regret it by month six. Here's the hiring sequence that actually works, and the mistakes worth avoiding before they cost you a Series B.
Mihai GolovatencoMihai GolovatencoTalent Director