The declare builtin sets variables and assigns attributes that control
their behavior — integer-only, read-only, uppercase, lowercase, indexed array,
associative array, and more. It gives bash variables a degree of type safety
and structure that plain assignment cannot provide.
declare is also used to inspect variables —
listing all defined variables and functions, or displaying a variable's current
attributes and value. The synonym typeset is identical and exists
for ksh compatibility.
Integer — arithmetic only, non-numeric assignments become 0
Read-only — cannot be changed or unset after declaration
Export — mark for export to child processes (like export)
Uppercase — value auto-converted to uppercase on assignment
Lowercase — value auto-converted to lowercase on assignment
Indexed array — standard numerically-indexed array
Associative array — key-value map (hash/dictionary)
Nameref — variable is a reference to another variable
Global — declare at global scope even inside a function
Print — display current value and attributes
Function — operate on functions, not variables
Remove an attribute (e.g., +r removes read-only)
- to set an attribute,
+ to remove it. For example declare -r VAR makes
it read-only; declare +x VAR removes the export attribute.
Note: read-only (-r) cannot be removed once set.
Integer-attributed variables only hold numeric values and support
direct arithmetic without $(( )):
# Declare integer variables declare -i count=0 declare -i total=100 declare -i pages # Arithmetic works directly count=count+5 total=total*2 echo "count=$count total=$total" # Non-numeric assignment becomes 0 count="hello" echo "count=$count" # prints 0 # Practical: loop counter declare -i errors=0 for logfile in /var/log/*.log; do grep -q "ERROR" "$logfile" && errors=errors+1 done echo "Files with errors: $errors"Output:
-i, you can write
count=count+1 instead of count=$((count+1)).
The variable is always treated as an arithmetic expression on assignment.
Constants that cannot be changed or unset — ideal for configuration values:
# Declare constants at top of script declare -r SCRIPT_VERSION="2.1.4" declare -r CONFIG_DIR="/etc/myapp" declare -r LOG_FILE="/var/log/myapp.log" declare -r MAX_RETRIES=3 # Attempting to change raises an error SCRIPT_VERSION="3.0" # bash: SCRIPT_VERSION: readonly variable unset SCRIPT_VERSION # bash: unset: SCRIPT_VERSION: cannot unset # Combine with -i for a read-only integer declare -ri MAX_CONNECTIONS=100
-r is set, it cannot
be removed with +r — the variable is read-only for the life of
the shell session. Declare constants early and intentionally.
declare -r for constants is
more explicit and safer than plain assignment. It documents intent and prevents
accidental overwrite later in a long script.
Auto-case conversion on every assignment — no need to call
tr or parameter expansion:
# Uppercase — any assignment is auto-converted declare -u HOSTNAME_UPPER HOSTNAME_UPPER=$(hostname) echo "$HOSTNAME_UPPER" # BAMBUSTUDIO # Lowercase — useful for normalizing user input declare -l username read -p "Enter username: " username # Whatever they type becomes lowercase echo "Normalized: $username" # Practical: normalize environment variable declare -l log_level log_level="${LOG_LEVEL:-info}" case "$log_level" in debug) echo "Verbose logging on" ;; info) echo "Standard logging" ;; warn) echo "Warnings only" ;; *) echo "Unknown level: $log_level" ;; esacOutput:
-u and -l reliable normalizers throughout a script's life.
Explicitly declare indexed arrays — good practice even though bash creates them implicitly:
# Explicit array declaration declare -a servers declare -a -r ENVIRONMENTS=("dev" "staging" "prod") # read-only array # Populate servers=("web01" "web02" "db01" "db02") servers+=("cache01") # Iterate for srv in "${servers[@]}"; do echo "Checking: $srv" done # Inspect with declare -p declare -p serversdeclare -p output:
declare -p varname
shows the exact internal representation of any variable — attributes, indices,
and values. Invaluable for debugging arrays that aren't behaving as expected.
Key-value maps — bash's equivalent of a dictionary or hash.
Must be explicitly declared with -A:
# Must declare -A before use — no implicit creation declare -A server_roles declare -A port_map declare -A config # Assign key-value pairs server_roles["web01"]="nginx" server_roles["db01"]="postgresql" server_roles["cache01"]="redis" # Or assign all at once declare -A port_map=( [http]=80 [https]=443 [ssh]=22 [mysql]=3306 [redis]=6379 ) # Lookup echo "web01 runs: ${server_roles[web01]}" echo "HTTPS port: ${port_map[https]}" # Iterate keys and values for service in "${!port_map[@]}"; do printf "%-10s %s\n" "$service" "${port_map[$service]}" done # Check if key exists if [[ -v port_map[redis] ]]; then echo "Redis port defined: ${port_map[redis]}" fiOutput:
declare -A and trying to
use string keys will silently fail or produce unexpected results.
declare -x is equivalent to export — marks a variable
for inheritance by child processes:
# These are equivalent export PATH="/usr/local/bin:$PATH" declare -x PATH="/usr/local/bin:$PATH" # Combine attributes — exported read-only integer declare -xri MAX_PROCS=8 # Export an array (note: arrays are NOT exported to subshells) declare -x DB_HOST="db.example.com" declare -x DB_PORT="5432" declare -x DB_NAME="production" # Remove export attribute without unsetting variable declare +x DB_NAME # DB_NAME still exists locally but won't be inherited by children # List all exported variables declare -x
export SERVERS="${servers[*]}" and re-split in the child.
Namerefs create a reference to another variable by name — useful for passing variable names into functions:
# Basic nameref declare -n ref=myvar myvar="hello" echo "$ref" # prints: hello ref="world" echo "$myvar" # prints: world (same variable) # Practical — function that modifies a caller's variable by name fill_array() { declare -n _arr=$1 # _arr is a nameref to whatever name was passed _arr=("alpha" "beta" "gamma") } declare -a my_list fill_array my_list echo "${my_list[@]}" # alpha beta gamma # Another pattern — function returns result into named variable get_hostname() { declare -n _result=$1 _result=$(hostname -f) } get_hostname FQDN echo "This host: $FQDN"
_arr) to avoid
colliding with the variable name passed in. If the caller passes
arr and your nameref is also named arr, bash
will complain about a circular reference.
declare is also an inspection tool — see what is defined and how:
# Inspect a single variable — shows attributes and value declare -p PATH declare -p BASH_VERSION declare -p myarray # Inspect all currently declared variables declare -p # List all defined functions (names only) declare -F # Show full function definitions declare -f # Show definition of one specific function declare -f cleanup # Practical — capture current environment to restore later declare -p > /tmp/env_snapshot.sh # Later, in another script: source /tmp/env_snapshot.shdeclare -p examples:
-rx means read-only and exported, -a means indexed array,
-A means associative array. This output is also valid bash — you can
source it to recreate the variable exactly.
Variables declared with declare inside a function are
local to that function by default — same as using local:
#!/bin/bash my_function() { declare local_var="I am local" # local to function declare -g global_var="I am global" # -g forces global scope declare -i local_count=0 # local integer echo "Inside: $local_var" } my_function echo "Outside local_var: '${local_var}'" # empty — was local echo "Outside global_var: '$global_var'" # visible — was -gOutput:
declare
and local both create local variables. The difference is that
declare can set attributes (-i, -r, etc.)
while local cannot. Use declare -g when you need a
global variable with attributes set from inside a function.
| Command | What it does |
|---|---|
| declare -i VAR=0 | Integer variable — arithmetic on assignment |
| declare -r VAR="x" | Read-only constant — cannot be changed |
| declare -x VAR="x" | Export to child processes |
| declare -u VAR | Auto-uppercase on assignment |
| declare -l VAR | Auto-lowercase on assignment |
| declare -a ARR | Indexed array declaration |
| declare -A MAP | Associative array (required — no implicit creation) |
| declare -n REF=OTHER | Nameref — alias for another variable |
| declare -g VAR | Global scope even inside a function |
| declare -ri VAR=N | Combine flags — read-only integer |
| declare -p VAR | Print variable with all attributes |
| declare -p | Print ALL declared variables |
| declare -f | Print all function definitions |
| declare -F | Print function names only |
| declare +x VAR | Remove export attribute |
| Builtin | Relationship to declare |
|---|---|
| typeset | Identical to declare — ksh compatibility synonym |
| local | Creates function-local variables but cannot set attributes |
| export | Equivalent to declare -x — marks variable for export |
| readonly | Equivalent to declare -r — sets read-only attribute |
| unset | Removes a variable or function (blocked by -r) |