Bash Parameters and Variables: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
 
(38 intermediate revisions by the same user not shown)
Line 2: Line 2:


* [[bash Concepts#bash_Built-In_Variables|bash Concepts]]
* [[bash Concepts#bash_Built-In_Variables|bash Concepts]]
* [[bash Built-In Variables]]
* [[Bash_Parameter_and_Variable_Expansion|bash Parameter and Variable Expansion]]


=Parameters and Variables=
=Parameters and Variables=
Line 10: Line 10:
Parameters can be designated by a number (e.g. 1), a special character (e.g. *), or a name (e.g. blue). Parameters designated by a name are referred to as [[#Variables|variables]]. The variables that have a special meaning to the shell (for example "IFS") are known as internal, keyword or [[#Built-in_Variables|built-in variables]]. Non-variable parameters can be further categorized in [[#Positional_Parameters|positional parameters]], which are the shell's command line arguments, retrievable as $1, $2, etc. and [[#Special_Parameters|special parameters]], that are denoted by special characters.
Parameters can be designated by a number (e.g. 1), a special character (e.g. *), or a name (e.g. blue). Parameters designated by a name are referred to as [[#Variables|variables]]. The variables that have a special meaning to the shell (for example "IFS") are known as internal, keyword or [[#Built-in_Variables|built-in variables]]. Non-variable parameters can be further categorized in [[#Positional_Parameters|positional parameters]], which are the shell's command line arguments, retrievable as $1, $2, etc. and [[#Special_Parameters|special parameters]], that are denoted by special characters.


=Parameter and Variable Expansion - Accessing a Parameter's or Variable's Value=
Also see: {{Internal|Variables,_Parameters,_Arguments#Variable|Variables, Parameters, Arguments}}


A dollar sign ($) that is NOT followed by an open parenthesis initiates parameter or variable expansion. The simplest case of expansion is retrieving the parameter or variable value: the associated value is retrieved by prefixing the parameter/variable's number, character or name with $ or enclosing it in ${...}
=<span id='Variable_Expansion'></span><span id='Parameter_and_Variable_Expansion'></span>Parameter and Variable Expansion - Accessing a Parameter's or Variable's Value=


==Difference between $ and ${...}==
A dollar sign ($) that is NOT followed by an open parenthesis initiates parameter or variable expansion. The simplest case of expansion is retrieving the parameter or variable value: the associated value is retrieved by prefixing the parameter/variable's number, character or name with $ or enclosing it in ${...}. bash supports complex parameter expansion rules, documented here: {{Internal|bash Parameter and Variable Expansion|Parameter and Variable Expansion}}
<font color=darkgray>TODO</font>


=<span id='Positional_Parameters'></span><span id='#.241.2C_.242.2C_...'></span>Positional Parameters $1, $2, ...=
=<span id='Positional_Parameters'></span><span id='#.241.2C_.242.2C_...'></span>Positional Parameters $1, $2, ...=
Line 31: Line 30:
  ${10}
  ${10}


Positional parameters are assigned from the shell’s arguments when it is invoked. They may not be assigned to with assignment statements, but they can be reassigned with [[Bash_set#set_and_Positional_Parameters|set]] builtin command and they can be shifted left with the bash built-in command [[bash shift|shift]].
Positional parameters are assigned from the shell’s arguments when it is invoked. They may not be assigned to with assignment statements, but they can be reassigned with [[Bash_set#set_and_Positional_Parameters|set]] [[Bash_Concepts#Builtin|builtin]] command and they can be shifted left with the bash built-in command [[bash shift|shift]].


The positional parameters are temporarily replaced when a [[Bash_Functions#Arguments|shell function is executed]].
The positional parameters are temporarily replaced when a [[Bash_Functions#Arguments|shell function is executed]].
Line 50: Line 49:
=Special Parameters=
=Special Parameters=


For a list of special parameters, see:
{{Internal|bash Special Parameters|Special Parameters}}
{{Internal|bash Special Parameters|bash Special Parameters}}


=Variables=
=Variables=
A variable is a shell [[#Parameter|parameter]] designated by a name, not a number or a special character. Variables can be user-created, or built-in.
A variable is a shell [[#Parameter|parameter]] designated by a name, not a number or a special character. Variables can be user-created, or [[#Built-in_Variables|built-in]] (also known as internal or keyword variables).
 
==<span id='Built-In_Environment_Variables'></span><span id='Built-In Environment Variables'></span><span id='Built-In_Variables'></span><span id='Built-In_Variables_2'></span>Built-in Variables==
 
The variables that have a special meaning to the shell (for example "IFS") are known as internal, keyword or built-in variables.
{{Internal|Bash_Built-In_Variables|Built-in Variables}}


==Variable Assignment==
==Variable Assignment==
Line 63: Line 66:
  blue=10
  blue=10


==<span id='Built-In_Environment_Variables'></span><span id='Built-In Environment Variables'></span><span id='Built-In_Variables'></span><span id='Built-In_Variables_2'></span>Built-in Variables==
If the value of the variable is obtained with [[Bash Command Substitution#Overview|command substitution]], then the exit code of the assignment operation is given by the exit value of the command:
 
The variables that have a special meaning to the shell (for example "IFS") are known as internal, keyword or built-in variables. For a list of built-in variables, see:
{{Internal|Bash_Built-In_Variables|Built-in Variables}}
 
=<span id='Variable_Expansion'></span>Parameter and Variable Expansion=
 
 
 
 
'''Parameters''' include:
* Command line (positional) parameters: $1, $2, ...
* Special parameters:
 
'''Variables''' include:
* User-created variables
* Keyword variables
 
Parameters and variables are not expanded if the string they are included in are enclosed within [[Bash_Concepts#Single_Quotes|single quotes]] or if the leading dollar sign is escaped. Parameters and variables are expanded if the string they are included in is enclosed within [[Bash_Concepts#Double_Quotes|double quotes]].
 
Parameter and variable expansion is a particular case of bash command line expansion:
 
{{Internal|Bash_Command_Line_Expansion#Parameter_and_Variable_Expansion|bash Command Line Expansion}}
 
==Expand to a Default Value==
 
{{External|http://wiki.bash-hackers.org/syntax/pe#use_a_default_value}}


<syntaxhighlight lang='bash'>
<syntaxhighlight lang='bash'>
${var:-alternative}
if blue=$(command-that-produces-blue); then
  #
  # command succeeded,  'blue' is assigned with the content sent to the standard output of the command (if any)
  #
else
  #
  # command failed, 'blue' is assigned with the content sent to the standard output of the command (if any)
  #
fi
</syntaxhighlight>
</syntaxhighlight>


'''The :- form''' : if the variable 'var' referred in the expression was not defined, or it is the empty string, the expression expands the alternative.
Also see [[Bash_Parameters_and_Variables#Local_Variable_Assignment_and_Failure|Local Variable Assignment and Failure]] below.


<syntaxhighlight lang='bash'>
==Undeclared Variable==
${var-alternative}
</syntaxhighlight>
 
'''The - form''': if the colon is omitted, the expression is expanded to the alternative value only if the variable is not defined, not when it was empty.
 
==Assign a Default Value==
 
{{External|http://wiki.bash-hackers.org/syntax/pe#assign_a_default_value}}
 
<syntaxhighlight lang='bash'>
${var:=word}
</syntaxhighlight>
 
<syntaxhighlight lang='bash'>
${var=word}
</syntaxhighlight>
 
This expression works like [[#Expand_to_a_Default_Value|expansion to a default value]], but the word is not only expanded, but also '''assigned''' to the variable, if it was unset or null. The first form work if the variable is unset or an empty string, the second only if the parameter was unset.
 
==Use an Alternate Value==
 
{{External|http://wiki.bash-hackers.org/syntax/pe#use_an_alternate_value}}
 
<syntaxhighlight lang='bash'>
${var:+alternative}
</syntaxhighlight>
 
This form expands to nothing if the parameter is unset or empty.
 
<syntaxhighlight lang='bash'>
${var+alternative}
</syntaxhighlight>
 
This form expands to nothing if the variable is unset, and to the alternative if the variable is empty.
 
==Variable Expansion in an Arbitrary File==
 
If an arbitrary text file contains environment variable declarations, they can be expanded with [[envsubst]]. No other shell command line expansions will be performed. More details:
 
{{Internal|envsubst#Overview|envsubst}}
 
==String Length==
 
The length of a variable's value:
 
<syntaxhighlight lang='bash'>
${#var}
</syntaxhighlight>
 
=<span id='Indirect_Expansion'></span><span id='Variable_Indirection'></span>$!... Variable Indirection, Indirect Expansion=
 
If the first character of parameter is an exclamation point (!), a level of variable indirection is introduced.  Bash uses the value of the variable formed from the rest of parameter as the name of the variable; this variable is then expanded and that value is used in the  rest  of  the  substitution, rather than the value of parameter itself.  This is known as indirect expansion. The exceptions to this are the expansions of ${!prefix*} and ${!name[@]} described below. The exclamation point must immediately follow immediately after the left brace in order to introduce indirection. Example:
 
a="b"
b="blah"
echo ${!a}
 
produces "blah".
 
Note that assigning a value to a indirect variable does not need to use "${!...}". For the above example, we can simply:
 
${a}=blah
 
This will assign "blah" to a variable named "b".
 
In each of the cases below, word is subject to tilde expansion, parameter expansion, command substitution, and arithmetic expansion.
 
When not performing substring expansion, using the forms documented below, bash tests for a parameter that is unset or null.  Omitting the colon results in a test only for a parameter that is unset.
 
=Undeclared Variable=  


An undeclared variable is equivalent with an empty string when referenced. There is no error when such a variable is referenced in a bash program:
An undeclared variable is equivalent with an empty string when referenced. There is no error when such a variable is referenced in a bash program:
Line 174: Line 89:
  ><
  ><


=Uninitialized Variable=
==Uninitialized Variable==


An uninitialized variable is equivalent with an empty string when referenced. There is no error when such a variable is referenced in a bash program:
An uninitialized variable is equivalent with an empty string when referenced. There is no error when such a variable is referenced in a bash program:
Line 182: Line 97:
  ><
  ><


=Global Variable=
==Global Variable==
 
Global variables are only maintained within the context of the current shell. When the shell exits, the global variables are discarded.


Global variables are only maintained within the context of the current shell. Once the shell exits, the global variables are discarded.
Once a global variable was declared in a shell, thus placed in the shell's environment, the variable is visible inside the functions executed within that shell and inside functions invoked from function invoked from the shell, recursively. If a function declares a global variable, the variable is placed in the shell's environment and it will be available to all subsequent functions invoked (possibly recursively) after that.


The global variables declared in a shell are visible inside the functions executed within that shell and inside functions invoked from function invoked from the shell, recursively.  
There's an utility named [[Envsubst#Overview|envsubst]] that can be used to perform variable substitution in text files.


The value of a global variable is ''not'' available to sub-shells, unless the variable is ''exported'', with the [[#export]] keyword.
The value of a global variable is ''not'' available to [[bash Concepts#Subshell|subshell]], unless the variable is ''exported'', with the [[#export|export]] keyword. If a function is invoked from a subshell, with $(...), it will not have access to non-exported global variables of the parent shell.


==<tt>export</tt>==
===<tt>export</tt>===


<pre>
<pre>
Line 196: Line 113:
</pre>
</pre>


Declaring a global variable to be exported causes the variable and its value ''to be copied'' in the environment of a sub-shell. The sub-shell gets a copy of the variable, not a reference. The sub-shell has no access to the parent process's environment whatsoever. Modifying the variable from the sub-shell does not reflect into the value of the global variable maintained by the invoking shell. When the shell sub-process terminates any changes made to its environment are lost. There is no way to modify directly the parent's environment.
Declaring a global variable to be exported causes the variable and its value ''to be copied'' in the environment of a [[bash Concepts#Subshell|subshell]]. The subshell gets a copy of the variable, not a reference. The subshell has no access to the parent process's environment whatsoever. Modifying the variable from the subshell does not reflect into the value of the global variable maintained by the invoking shell. When the shell sub-process terminates any changes made to its environment are lost. There is no way to modify directly the parent's environment.


==Variables exported by Sub-Shells==
===Variables exported by Sub-Shells===


If a sub-shell invoked from another shell exports a global variable, once the sub-shell exits, the exported variables are discarded, and the invoking shell does not sees them.
If a sub-shell invoked from another shell exports a global variable, once the [[bash Concepts#Subshell|subshell]] exits, the exported variables are discarded, and the invoking shell does not sees them.
 
===To Explain===
 
<font color=darkgray>
The following code displays "blue" twice, even if the second execution happens in a [[bash Concepts#Subshell|subshell]], which is supposed to not see the global variable, unless is exported. Why?
<syntaxhighlight lang='bash'>
COLOR=blue
echo ${COLOR}
(echo ${COLOR})
</syntaxhighlight>
</font>


=Local Variable=
==Local Variable==


If a variable is declared inside a function, without any qualifier, '''it automatically becomes a global variable''', and it is visible to the entire shell after the function execution, even after the function exits.
If a variable is declared inside a function, without any qualifier, '''it automatically becomes a global variable''', and it is visible to the entire shell after the function execution, even after the function exits.


In order to prevent a variable declared inside of a function to become global, it must be declared as local: the <tt>local</tt> keyword designates a local variable. Local variables can only be declared inside a function.
In order to prevent a variable declared inside of a function to become global, it must be declared as local: the <tt>local</tt> keyword designates a local variable. Local variables can only be declared inside a function. A local variable is visible to the scope in which was declared in, and to all functions recursively invoked from that scope after the variable has been declared.


Example:
Example:


<pre>
<syntaxhighlight lang='bash'>
local a=b
local a=b
</pre>
</syntaxhighlight>


==Local Variable Assignment and Failure==
===Local Variable Assignment and Failure===


{{Warn|Do not do a local variable assignment from a function that may return non-zero and expect to use the return value to exit}}
{{Warn|Do not do a local variable assignment from a function that may return non-zero and expect to use the return value to exit}}
Line 224: Line 152:
</pre>
</pre>


Even if do_something returns a non-zero error code, that is not detected and exit is not executed.
Even if do_something returns a non-zero error code, that is not detected and exit is not executed. {{Note|This is because the return value that is being tested is not generated by the command execution, but by the '<tt>local a</tt>' declaration, which is always successful.}}


Do this instead:
Do this instead:
Line 233: Line 161:
</pre>
</pre>


=Listing Declared Variables=
==Listing Declared Variables==


Use [[bash declare/typeset|declare]] or [[bash declare/typeset|typeset]]:
Use [[bash declare/typeset|declare]] or [[bash declare/typeset|typeset]]:
Line 248: Line 176:
</pre>
</pre>


=Booleans=
==Booleans==


Recommended usage pattern:
Recommended usage pattern:
Line 254: Line 182:
Declaration:
Declaration:


<pre>
<syntaxhighlight lang='bash'>
some_var=true
some_var=true
</pre>
</syntaxhighlight>


Usage:
Usage:


<pre>
<syntaxhighlight lang='bash'>
${some_var} && echo "this will execute"
${some_var} && echo "this will execute"


Line 266: Line 194:
   echo "this will execute"
   echo "this will execute"
fi
fi
</pre>
</syntaxhighlight>


Do not use:
Do not use:


<pre>
<syntaxhighlight lang='bash'>
if [ ${some_var} ]; then ...
if [ ${some_var} ]; then ...
</pre>
</syntaxhighlight>


This will always evaluate to "true" regardless of the some_var value.
This will always evaluate to "true" regardless of the some_var value.
Negation with "!" works:
<syntaxhighlight lang='bash'>
if ! ${some_var}; then
  echo "something"
else
echo "something else"
fi
</syntaxhighlight>

Latest revision as of 18:57, 17 August 2023

Internal

Parameters and Variables

Shell stores values internally using entities named parameters.

Parameters can be designated by a number (e.g. 1), a special character (e.g. *), or a name (e.g. blue). Parameters designated by a name are referred to as variables. The variables that have a special meaning to the shell (for example "IFS") are known as internal, keyword or built-in variables. Non-variable parameters can be further categorized in positional parameters, which are the shell's command line arguments, retrievable as $1, $2, etc. and special parameters, that are denoted by special characters.

Also see:

Variables, Parameters, Arguments

Parameter and Variable Expansion - Accessing a Parameter's or Variable's Value

A dollar sign ($) that is NOT followed by an open parenthesis initiates parameter or variable expansion. The simplest case of expansion is retrieving the parameter or variable value: the associated value is retrieved by prefixing the parameter/variable's number, character or name with $ or enclosing it in ${...}. bash supports complex parameter expansion rules, documented here:

Parameter and Variable Expansion

Positional Parameters $1, $2, ...

A positional parameter is a parameter denoted by one or more digits, other than the single digit 0 (the single digit 0 denotes the special parameter $0).

bash allows only nine parameters to be referenced directly (n = 1–9) as in:

$1

$1 is equivalent with ${1}.

When a positional parameter consisting of more than a single digit is expanded, it must be enclosed in braces. For n values greater than 9, the command line arguments must be specified as ${n}:

${10}

Positional parameters are assigned from the shell’s arguments when it is invoked. They may not be assigned to with assignment statements, but they can be reassigned with set builtin command and they can be shifted left with the bash built-in command shift.

The positional parameters are temporarily replaced when a shell function is executed.

Iterating over Positional Paramenters

Solution 1

while [ -n "$1" ]; do
  ...
  shift
done

Solution 2

Use an Array

Special Parameters

Special Parameters

Variables

A variable is a shell parameter designated by a name, not a number or a special character. Variables can be user-created, or built-in (also known as internal or keyword variables).

Built-in Variables

The variables that have a special meaning to the shell (for example "IFS") are known as internal, keyword or built-in variables.

Built-in Variables

Variable Assignment

Variable Assignment

Unlike positional parameters and special parameters, variable can be assigned values by using the "=" character, with no spaces before or after:

blue=10

If the value of the variable is obtained with command substitution, then the exit code of the assignment operation is given by the exit value of the command:

if blue=$(command-that-produces-blue); then
  #
  # command succeeded,  'blue' is assigned with the content sent to the standard output of the command (if any)
  #
else
  #
  # command failed, 'blue' is assigned with the content sent to the standard output of the command (if any)
  #
fi

Also see Local Variable Assignment and Failure below.

Undeclared Variable

An undeclared variable is equivalent with an empty string when referenced. There is no error when such a variable is referenced in a bash program:

echo ">${no_such_var}<" 
><

Uninitialized Variable

An uninitialized variable is equivalent with an empty string when referenced. There is no error when such a variable is referenced in a bash program:

local some_var
echo ">${some_var}<" 
><

Global Variable

Global variables are only maintained within the context of the current shell. When the shell exits, the global variables are discarded.

Once a global variable was declared in a shell, thus placed in the shell's environment, the variable is visible inside the functions executed within that shell and inside functions invoked from function invoked from the shell, recursively. If a function declares a global variable, the variable is placed in the shell's environment and it will be available to all subsequent functions invoked (possibly recursively) after that.

There's an utility named envsubst that can be used to perform variable substitution in text files.

The value of a global variable is not available to subshell, unless the variable is exported, with the export keyword. If a function is invoked from a subshell, with $(...), it will not have access to non-exported global variables of the parent shell.

export

export VAR=VALUE

Declaring a global variable to be exported causes the variable and its value to be copied in the environment of a subshell. The subshell gets a copy of the variable, not a reference. The subshell has no access to the parent process's environment whatsoever. Modifying the variable from the subshell does not reflect into the value of the global variable maintained by the invoking shell. When the shell sub-process terminates any changes made to its environment are lost. There is no way to modify directly the parent's environment.

Variables exported by Sub-Shells

If a sub-shell invoked from another shell exports a global variable, once the subshell exits, the exported variables are discarded, and the invoking shell does not sees them.

To Explain

The following code displays "blue" twice, even if the second execution happens in a subshell, which is supposed to not see the global variable, unless is exported. Why?

COLOR=blue
echo ${COLOR}
(echo ${COLOR})

Local Variable

If a variable is declared inside a function, without any qualifier, it automatically becomes a global variable, and it is visible to the entire shell after the function execution, even after the function exits.

In order to prevent a variable declared inside of a function to become global, it must be declared as local: the local keyword designates a local variable. Local variables can only be declared inside a function. A local variable is visible to the scope in which was declared in, and to all functions recursively invoked from that scope after the variable has been declared.

Example:

local a=b

Local Variable Assignment and Failure


Do not do a local variable assignment from a function that may return non-zero and expect to use the return value to exit

Do not do something like this:

local a=$(do_something) || exit 1 # hoping to exit if 'do_something' returns a non-zero value

Even if do_something returns a non-zero error code, that is not detected and exit is not executed.


This is because the return value that is being tested is not generated by the command execution, but by the 'local a' declaration, which is always successful.

Do this instead:

local a;
a=$(do_something) || exit

Listing Declared Variables

Use declare or typeset:

declare -p

A way of obtaining the value for a specific variable is:

local var_name="HISTSIZE"
declare -p | grep "declare .. ${var_name}" | sed -e 's/.*=//'

Booleans

Recommended usage pattern:

Declaration:

some_var=true

Usage:

${some_var} && echo "this will execute"

if ${some_var}; then
   echo "this will execute"
fi

Do not use:

if [ ${some_var} ]; then ...

This will always evaluate to "true" regardless of the some_var value.

Negation with "!" works:

if ! ${some_var}; then
  echo "something"
else
 echo "something else"
fi