opscape

Shell builtins · bash builtin

declare

Create variables with attributes (array, associative array, integer, read-only, exported), or show how variables are defined.

declare [-aAfFgiIlnrtux] [-p] [NAME[=VALUE] ...]

10 of 10 examples run in Debian 13.7 on 2026-10-31 · manual: bash(1), SHELL BUILTIN COMMANDS

Plain assignment (x=1) creates an ordinary string variable. declare creates a variable with attributes: -a indexed array, -A associative array (the only way to get one), -i integer, -r read-only, -x exported, -l/-u forced lower or upper case, and -n a name reference to another variable. typeset is an older synonym kept for ksh compatibility.

Inside a function, declare makes the variable local to that function, exactly like local. Add -g to create or modify a global from within a function instead.

declare -p NAME is the best way to inspect a variable: it prints a command that would recreate it, with its attributes and exact quoting, so empty strings, arrays and odd characters are all visible. declare -f prints function definitions and declare -F only their names.

Examples

Inspect a variable exactly with -p

name="Alice Moreau"
empty=""
list=(one "two words" three)
declare -p name empty list
output
declare -- name="Alice Moreau"
declare -- empty=""
declare -a list=([0]="one" [1]="two words" [2]="three")

-p shows each variable as a declare command, with attributes (-a for the array) and quoting. It makes the difference between an empty string and a value with spaces obvious, which echo hides.

Create an associative array with -A

declare -A port=([web]=80 [db]=5432)
port[cache]=6379
for k in "${!port[@]}"; do echo "$k -> ${port[$k]}"; done | sort
output
cache -> 6379
db -> 5432
web -> 80

Associative arrays use strings as keys and must be declared with -A first. ${!port[@]} lists the keys. Their order is not defined, hence the sort.

Forgetting -A gives an indexed array instead

colors[red]=ff0000
colors[green]=00ff00
declare -p colors
output
declare -a colors=([0]="00ff00")

Without declare -A, colors is an indexed array and red and green are evaluated as arithmetic, where an unknown name is 0. Both assignments go to index 0, and the second overwrites the first.

Make a variable evaluate arithmetic with -i

declare -i total=10
total+=5
total=total*2
echo "$total"
total="hello"
echo "after assigning text: $total"
output
30
after assigning text: 0

With -i, every assignment is evaluated as arithmetic, so += adds rather than appending and total*2 multiplies. A string that is not a number evaluates to 0, without an error. Many people prefer explicit $(( )) for that reason.

Make a constant with -r

declare -r MAX_RETRIES=3
MAX_RETRIES=5
echo "still $MAX_RETRIES"
output
/bin/bash: line 2: MAX_RETRIES: readonly variable
still 3

Assigning to a read-only variable is an error (status 1, the script continues unless set -e is on). readonly NAME=value is the POSIX spelling of the same thing.

Force lower or upper case with -l and -u

declare -l answer
declare -u code
answer="YeS"; code="eu-west-1"
echo "$answer $code"
output
yes EU-WEST-1

The case conversion is applied on every assignment, which makes comparisons such as [[ $answer == yes ]] robust against how the user typed it.

Refer to another variable by name with -n

add_item() {
  declare -n list=$1
  list+=("$2")
}
fruits=(apple)
add_item fruits banana
add_item fruits cherry
declare -p fruits
output
declare -a fruits=([0]="apple" [1]="banana" [2]="cherry")

list becomes an alias for the variable whose name is in $1, so the function modifies the caller's array. Namerefs (bash 4.3+) replace most uses of eval for this.

declare inside a function makes a local variable

count=1
f() { declare count=99; echo "inside: $count"; }
g() { declare -g count=42; }
f; echo "after f: $count"
g; echo "after g: $count"
output
inside: 99
after f: 1
after g: 42

In a function, declare behaves like local, so f leaves the global alone. -g forces a global, which is how g changes it.

List function names or definitions

source src/util.sh
declare -F
declare -f log
output
declare -f die
declare -f log
log () 
{ 
    printf '%s %s\n' "$(date +%H:%M:%S)" "$*" 1>&2
}

-F prints only the names of defined functions; -f NAME prints the full definition, as bash has reformatted it.

Export with -x and remove attributes with +

declare -x REGION=eu
bash -c 'echo "child: $REGION"'
declare +x REGION
bash -c 'echo "child: [$REGION]"'
output
child: eu
child: []

-x is the same as export. A plus sign removes an attribute, so +x stops exporting while keeping the value in the shell.

Common pitfalls

  • Assigning to arr[key] without declare -A silently creates an indexed array, collapsing every non-numeric key into index 0.

  • declare inside a function is local. A function meant to set up globals needs declare -g.

  • declare -i turns any non-numeric string into 0 without an error, hiding bad input.

  • declare x=$(cmd) returns declare's status, not cmd's, so set -e misses a failure. Declare and assign on separate lines.

GNU vs BSD and macOS

The examples above ran the GNU/Linux version. These are the differences that matter on macOS and the BSDs (written from their documentation, not run here).

  • declare is bash-only (zsh and ksh have typeset with similar but not identical options). Associative arrays need bash 4 or newer; macOS still ships bash 3.2 as /bin/bash, where declare -A fails. Install a newer bash or use zsh.

Options worth knowing

A selection, described in our own words. For the full list, see bash(1), SHELL BUILTIN COMMANDS.

OptionWhat it does
-aMake the names indexed arrays.
-AMake the names associative arrays (string keys).
-iGive the integer attribute: assignments are evaluated as arithmetic.
-rMake the names read-only.
-xExport the names to child processes.
-lConvert values to lower case on assignment.
-uConvert values to upper case on assignment.
-nMake the name a reference to the variable named by its value.
-gInside a function, create or modify a global variable.
-pPrint the attributes and value of each name as a reusable declare command.
-fAct on functions; with no other attribute, print their definitions.
-FPrint function names (and attributes) without their bodies.
-tGive the trace attribute, so functions inherit DEBUG and RETURN traps.
-IMake a new local variable inherit the value and attributes of the same name in an outer scope.

Related