When it comes to shell scripting, although I’ve developed on UNIX-like systems for years and there’s even been times when my primary task was writing shell scripts, I seem to only ever retain a level of understanding that “gets the job done”.
One quickly forgotten topic is the scoping behaviour of shell and environment variables. So I thought I’d refresh my knowledge and write about it.
Simple shell variables
First, some basics.
In the Bash shell (sh/bash), you define variables by declaring a key-value pair:
This gives you a shell variable, VAR1
, whose value you can see with echo
command, referencing the variable using the $
prefix:
You can use single quotes to handle significant spaces and to express reserved symbols. Single quotes provide strong quoting - everything is interpreted as a string.
Similary, you can use double quotes to handle significant spaces but also to perform variable expansion and command substitution. Double quotes provide weak quoting.
This quoting behaviour applies generally to Bash.
Variable scoping
The variable scope is characterised by the processes in which they are visible.
Processes are arranged in a tree structure with parent and child relationships between them: a process that starts another is the parent, and the newly created one is the child or sub-process.
Consequently variables are classified into two categories:
- Shell variables
- Environment variables
Shell variables are only visible to the current shell whereas environment variables are also visible to sub-processes of the shell.
Exporting shell variables to make environment variables
Previously section, we were defining shell variables which, by their nature, are only visible inside the current shell.
In Bash, you use the export
command to make a shell variable available to child processes - i.e. to make it an environment variable.
Let’s define a little script: print_env.sh
with execute permissions.
Running a script starts a child process which inherits its environment variables from the variables exported by the parent shell.
So if we don’t export the variable VAR1
, then the child process won’t see it.
Case in point:
VAR1
isn’t defined in the script’s shell.
So let’s make VAR1
visible by exporting it from the original shell and then rerunning the script:
Typically the definition and exporting are combined into the one command:
Some important caveats:
- The value received by the child is a copy of the parent’s variable taken at the time when the child process starts.
- You can’t modify the enviroment of a parent shell.
Unsetting and demoting
You can use the unset
command to delete a variable.
If you no longer want to export a variable, but still keep it defined in the current shell use export -n
Working with environment files
You can define an environment by adding the variable definitions to a file and using the source
command to apply it.
source
is a Bash shell built-in command that executes the content of the file passed as argument, in the current shell. The file can set shell variables and environment variables.
Let’s create a file, shell.env
Then initialise our current shell via source shell.env
If we’d instead put these variable definitions into an executable script and run it, then it would have initiated its own short-lived environment, rather than our current shell.
Environment wrappers
If you want to run a script with a specific environment without affecting the current shell, then use the env
command to supply a short-lived environment to a process.
For example:
That’s it for now. I hope you find these notes helpful!
This post is Creative Commons Attribution 4.0 International (CC BY 4.0) licensed.