Variables in Shell

This article talks about the variable scope in shell, especially how to access variables between different scripts.

Local Variables

The set command displays all functions and local variables defined for a specific process:

1
2
3
4
# this definition will be in `set`, not in `env`
HELLO=999
# run `set` will see `HELLO`
set

It also sorts the display alphabetically.

注意set中的variables 不一定出现在env中,顾名思义, env中显示的才是当前运行shell会被采用的variables.

ENV Variables

Environment variables are visible from the shell session and from any spawned child subshells, and also visable by the command launched from that shell. This makes environment variables useful in applications that create child subshells, which require parent shell information.

To view environment variables, use the env or the printenv command:

1
2
env | grep ^HOME
printenv HOME

You can use export to create environment variable.

1
export demo='hello world'

You can use unset to remove an existing environment variable.

1
unset demo

If you want to only set the variable for one command in one use, just prefix it, eg:

1
2
3
4
# EDITOR will only works for crontab command
EDITOR=vim crontab -e
# hot KUBECONFIG env variable
KUBECONFIG=xx.yml kubectl get nodes

A common trick for programmers is to include the single dot symbol in their PATH environment variable. The single dot symbol represents the current directory:

1
PATH=$PATH:.

Declare Command

Note that declare can print both set and env variables:

1
declare | grep ^HOME=

declare command can be used to create local variables in set or export to env:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# -i: integer
declare -i n=34
# can do arithmetic operations directly without let or expr
n=n/2
# the result is 17
echo $n

# -l: convert to lower case
declare -l hello=WORLD
# -u: convert to upper case
declare -u hello=world
# if reassign value to hello, will be convert to lower/upper case automatically

hello=world
# -p: display the attributes and values of each name
declare -p hello
## result is below, -- means local variable with no options compare to others like -x -i, etc
declare -- hello="world"

## declare can also export variable
declare -x apple=123
## will see apple in env
env | grep apple

declare -p apple
## result is below, -x means export variable
declare -x apple="123"

## remove from env
declare +x apple

You can mix the option:

1
2
3
4
5
6
declare -x -l EDITOR
EDITOR=vIM

declare -p EDITOR
## auto fix it to lower case
declare -xl EDITOR="vim"

Other ways to convert char to lower/upper case:

1
2
3
4
5
6
7
8
9
10
val=abcdEFG
# pattern is optional, eg [A-F]
# all to lower cases
echo ${val,,pattern}
# only first char to lower case
echo ${val,pattern}
# all to upper cases
echo ${val^^pattern}
# only first char to upper case
echo ${val^pattern}

Create constant (read-only) variable, cannot be unset and sustain for the shell session:

1
2
3
4
5
6
## set name as readonly
declare -r name=bob
## export it and readonly
declare -xr name=alice
## define a readonly array
declare -ra array=(1 2 3 4)

Declare different kind of arrays, see my other blog Array in Script:

1
2
3
4
## index-based
declare -a user_name
## associated-based
declare -A user_name

Shell Variables

Shell variables (actually the local variable) are available only in the shell that creates them. In fact, the Linux system also defines standard shell environment variables for you by default.

How to define a Shell environment variable: No space can appear between the variable, the equal sign, and the value:

1
var1=value

Variables defined within the shell script maintain their values throughtout the life of the shell script but are vanished when the shell script completes.

If you want to assign the value of one variable to another, must use $:

1
var2=${var1}

otherwise, var1 will be interpreted as text string.

1
var2=var1

Variable Scope

My question is how to pass variables from one script to another? Basically there are 3 options:

  1. Make the variable an environment variable (export it) before calling the child script (./script2).

  2. source the child script and it will run in the same shell, This would let you share more complex variables like arrays easily, but also means that the other script could modify variables in the caller shell.

  3. pass as parameters to child script: ./script2 var1 var2

  4. if use exec to replace current process, still need export variable.

Preserve ENV variables

Refer to this question. This is interesting and easy to make mistake, if I want to preserve global ENV variable when switch user in shell, use su xxx instead of su - xxx, - option will do:

  1. clears all environment variables except for TERM
  2. initializes the environment variables HOME, SHELL, USER, LOGNAME, PATH
  3. changes to the target user’s home directory
  4. sets argv[0] of the shell to ‘-’ in order to make the shell a login shell

But be very careful not let shell do variable expanding in command option:

1
su xxx -c "... echo $a"

Here $a will be expanded before executing, use single quote or escape the $ sign:

1
2
su xxx -c '... echo $a'
su xxx -c "... echo \$a"
0%