1
  1. What does

    declare name
    

    without providing any option do? Does it declare a string variable's name?

  2. What does

    declare -g
    

    without providing a variable name do? Does it display the values of all the variables with global attribute?

I didn't find answers in the description of declare at Bash manual.

Thanks.

  • I know I have seen that output in the past (declare -g), but it was not with the declare command... – Rui F Ribeiro Jul 25 '17 at 18:31
  • Tim, did any of the answers solve your problem? If so, please use the checkmark to tell the system that the question is Answered. Thank you! – Jeff Schaller Jul 30 '17 at 12:12
11

Variable handling and scoping in shell and especially bash can be very obscure and unintuitive (and sometimes buggy).

ksh had typeset for a similar feature. ksh, zsh, yash have typeset. bash has typeset as an alias of declare for compatibility with ksh, zsh has declare as an alias of typeset for compatibility with bash. Most shells have export, readonly and local that implement part of what typeset does.

One of the reasons why bash authors chose declare over typeset may be because typeset does not only set the type, it also declares a variable: introduce it in a given scope, possibly with a type, attributes and/or value.

In bash, variables can be:

  • unknown (like when they never have been set or declared)
  • declared (after declare)
  • set (when given a value, possibly empty).

They can be of different types:

  • scalar
  • array
  • associative array

and have several attributes:

  • integer
  • exported
  • readonly
  • all lower-case/upper-case
  • named references

(though the distinction between type and attribute can be quite blurry).

Not all combinations of types and attributes are supported or effective.

Now, declare declares the variable in the current scope. bash, even though it implements dynamic scoping treats the outer-most scope specially. It calls it the global scope.

declare behaves very differently when it's called in that global scope and when in a function (I'm not talking of the kind of separate scope that is introduced by subshells or associated with the environment).

When you do declare var inside a function and provided that same variable hadn't been declared in that same scope, it declares a new variable which is initially unset and that shadows a potential var variable that would have been present in the parent scope (the caller of the function).

That's dynamic scoping implemented via some sort of stack. When the function exits, the status, type, attributes and value of the variable as it was when the function was invoked is restored (popped from the stack).

Outside of any function however (in the global scope), declare does declare a variable, but does not initialise it as unset if it was set before (same as when using declare a second time within the same function scope). If a type is specified, the value of the variable may be converted, though not all conversion paths are allowed (only scalar to array/hash), and attributes may be added or removed.

In bash, within a function declare -g acts on the variable at the bottom of the stack, in the outer-most ("global") scope.

declare -g was inspired from ksh93's typeset -g. But ksh93 implements static scoping where the global scope is different and separate from each function scope. Doing the same with dynamic scoping makes little sense. In all other shells that have typeset -g (mksh, zsh, yash), typeset -g is used to change some attribute of a variable without instantiating a new local one.

In bash, people generally use it for the same purpose, but because it affects the variable of the outer-most scope instead of the current variable, it doesn't always work.

For instance:

integer() { typeset -gi "$1"; }

To make a variable an integer works in mksh/yash/zsh. It works in bash only on variables that have not been declared local by the caller:

$ bash -c 'f() { declare a; integer a; a=1+1; echo "$a"; }; integer() { typeset -gi "$1"; }; f'
1+1
$ bash -c 'f() { integer a; a=1+1; echo "$a"; }; integer() { typeset -gi "$1"; }; f'
2

Note that export var is neither typeset -x var nor typeset -gx var. It adds the export attribute without declaring a new variable if the variable already existed. Same for readonly vs typeset -r.

Also note that unset in bash only unsets a variable if it has been declared in the current scope (leaves it declared though except in the global scope; it removes attributes and values and the variable is no longer array or hash; also note that on namerefs, it unsets the referenced variable). Otherwise, it just pops one variable layer from the stack mentioned above. With bash 5.0 or above, that can be fixed by setting the localvar_unset option.

So to sum up:

 declare var

When called in a function and if var has not been declared before in that same function, declares a variable of type scalar with no attributes and that is initially unset.

If called outside of any function or if var had already been declared in the same function, it has no effect as we're not specifying any new type or attribute.

declare -g var

Wherever it's called would declare a var in the outer-most ("global") scope: make it declared, of type scalar, no attribute, no value if it was previously unknown in that scope (which for all intent and purpose is the same as an unknown variable except that it would show in the output of typeset -p), or do nothing otherwise.

In any case, you might not be able to access that variable in the context you're running that command:

f() { local a; g; }; g() { typeset -g a=123; echo "$a"; }; f

outputs nothing.

| improve this answer | |
  • Thanks. What documents did you or can I learn about scoping in bash or shells? – Tim Jul 28 '17 at 2:17
  • From the manpage of typeset of zsh, "the -g option forces variables to be created or modified at the global scope". Why "In all other shells that have typeset -g (mksh, zsh, yash), typeset -g is used to change some attribute of a variable without instantiating a new local one"? Why does f() { declare a; integer a; a=1+1; echo "$a"; }; integer() { typeset -gi "$1"; }; f in zsh output 2, instead of 1+1? – Tim Jul 28 '17 at 3:22
  • Is zsh dynamic scoping? – Tim Jul 28 '17 at 3:28
  • In bash, if I am correct, array and associative array are attributes of variables, and we can declare them with declare -a and declare -A. So what do you mean by saying array and associative array are types of variables? Are array and associative array still attributes of variables? – Tim Jul 28 '17 at 3:43
  • 2
    Yes, the distinction between type and attribute is blurry like I said. You could say that array/hash are attributes that can't be removed like readonly. In ksh88, scalars were just arrays with only the element of indice 0 set. It's very similar in bash, but bash still creates two different variables if you do a=1 and a[0]=1. zsh/yash are the ones with very distinct types for scalar and array. – Stéphane Chazelas Jul 28 '17 at 7:08
3
  1. declare name declares the variable named name, without any attribute; you can see its effect using ${name:not set} for example. The declared variable isn’t an array, and it doesn’t have the integer attribute set, so you could consider it a string variable, but it evaluates to 0 in arithmetic expressions.

  2. From the manpage:

    The -g option forces variables to be created or modified at the global scope, even when declare is executed in a shell function. It is ignored in all other cases.

    So declare -g with no parameters is the same as declare.

| improve this answer | |
  • Yeah, I was just reading that in the manual and you beat me with the answer. Curious factoid the output of set is byte-by-byte the same as declare ; I think Jeff also noticed that but his answer is not so clear. – Rui F Ribeiro Jul 25 '17 at 18:38
1
declare name

From https://www.gnu.org/software/bash/manual/bashref.html#Bash-Builtins

Declare variables and give them attributes.

You've not provided any options, so there are no attributes assigned, and the variable name is created. No value was provided, so none is assigned.

declare -g

The -g option forces variables to be created or modified at the global scope, even when declare is executed in a shell function. It is ignored in all other cases.

Since you aren't providing a name, no variable is created, so it is ignored. See for yourself:

declare -g > foo
declare > bar
diff foo bar

The only difference may be the value of the Bash variables $_, and possibly PIPESTATUS based on your previous commands; there is no other difference in the returned list of variables.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.