Learn › Linux Foundations › Bash Shell Environment
Part 1 of 2: shell variables, env, printenv, export, unset, PATH, which and type (Sections 8.1 to 8.10). No machine test here; it completes on the final step. The chapter lab waits at the end of Part 2.
Two copies of backup-status are installed. Which one runs when you type its bare name, and which value can that program see? Predict both results, inspect the shell environment and PATH order, then change one setting and check again.
Route: shell values and export first, then PATH, which, and type. Part 2 covers aliases and startup files.
This is Part 1 of 2. It covers Sections 8.1 to 8.10. You learn what a variable is and which of your variables a program can see. You learn how the shell turns a name into the program it runs, and the two tools that answer where a name came from. Part 2 covers Sections 8.11 to 8.18 and ends in the chapter lab, which covers Sections 8.1 to 8.17.
What you already know: Chapter 1 introduced the shell as the program that reads your line and runs it, and echo as the tool that prints. Chapter 5 read files with cat and head. Chapter 6 copied files with cp. This chapter is about the shell itself: what it remembers, what it passes on, and how it decides which program a word means.
Objectives
After this part you can:
Before you start a machine, each exercise shows a rehearsal terminal with recorded output. It checks the requested command but does not change Linux files. Start one real practice machine and use its admin@server1 shell for both parts. Commands run there, so an unintended command can still change that machine even if the lesson does not mark the exercise complete.
Variables, PATH changes and aliases belong to the shell that receives them. A new connection may start a fresh shell on the same machine, so check a needed setting in your current shell before relying on earlier work. This part teaches the commands for those checks. The final Test Room appears at the end of Part 2 on a separate prepared server and checks objectives automatically; rehearsal completion is not that result.
Five questions before the material. A wrong answer costs nothing: the explanation states the fact and names the section that teaches it.
>>> An assignment stores a value in the running shell under a name. Nothing is written to disk and no program runs. Section 8.1.
>>> A program is a separate process. It receives a copy of the environment, and only exported variables are in the environment. Sections 8.4 and 8.5.
>>> After checking whether a word is a shell command such as a builtin, Bash uses PATH to find an external program. PATH lists directories separated by colons. Bash searches left to right and uses the first executable match. Section 8.7.
>>> The search stops at the first match, so the order of the directories in PATH decides. Section 8.9.
>>> cd is a builtin: code inside the shell. Only the shell can change its own working directory, so no program on disk could do it. Section 8.10.
A variable is a name the shell holds a value under. You set it, you read it back, and it lives as long as that shell is running.
This chapter works in one directory, ~/tools. It holds two small programs, an alias file for Part 2, and two empty working directories. You are in your home directory. Start by listing tools:
prompt: admin@server1:~$ answer: ls tools ||| ls ~/tools ||| ls tools/ output: README.txt aliases.sh bin legacy receipts reports hint: Section 2.2. The listing command, then the directory tools (or ~/tools from anywhere).
Two directories that hold programs, bin and legacy; the alias file aliases.sh; receipts and reports, both empty; and a README.
You are in your home directory. Read tools/README.txt, which says what each of them is:
prompt: admin@server1:~$ answer: cat tools/README.txt ||| cat ~/tools/README.txt output: tools: the programs and files Chapter 8 (Bash Shell Environment) uses ===================================================================== bin/deploy-check prints the DEPLOY_TARGET it receives from its environment, and appends what it received to receipts/deploy-check.log bin/backup-status prints a fixed summary of the backups (version 2.0) legacy/backup-status the previous version (1.0) of the same program, kept to show how the order of PATH decides which one runs aliases.sh alias definitions to install as ~/.bash_aliases receipts/ where the programs record what they received reports/ empty; the brace expansion exercises create files here
A program in bin/ or legacy/ runs by its path (~/tools/bin/deploy-check) until its directory is added to PATH, then by its bare name. hint: Section 5.1. The printing command from Chapter 5, then the path tools/README.txt (or ~/tools/README.txt from anywhere).
Two programs and one alias file. deploy-check reports what it received from its environment. backup-status exists twice, in two directories, in two versions. Both write down what they were given, so the machine can check later what really happened.
The form is a name, an equals sign, and a value, with no spaces around the equals sign. The name is yours to choose. It may hold letters, digits and underscores, and it may not start with a digit. Capital letters are a convention, not a rule. An assignment touches no file, so it works wherever you stand. Set SITE_NAME to northwind:
prompt: admin@server1:~$ answer: SITE_NAME=northwind ||| SITE_NAME="northwind" ||| SITE_NAME='northwind' output: hint: Section 8.1. The name SITE_NAME, then the equals sign, then the value northwind, with nothing between them.
Silent. An assignment is not a command that reports; the shell stores the value and moves on. The value stays in this shell only, and it is gone when the shell ends.
A dollar sign in front of the name means the value of that name. The shell replaces $SITE_NAME with northwind before echo ever runs, so echo prints a word and never knows a variable was involved. Print the value of SITE_NAME:
prompt: admin@server1:~$ answer: echo $SITE_NAME ||| echo "$SITE_NAME" ||| echo ${SITE_NAME} output: northwind hint: Section 1.7. The printing command echo, then a dollar sign and the name SITE_NAME.
The value, with no name and no equals sign. This replacement is called expansion: the shell rewrites the line, then runs it.
Ask for a name nothing was ever stored under, NO_SUCH_NAME. The shell holds no file open for this, so it answers the same wherever you stand:
prompt: admin@server1:~$ answer: echo $NO_SUCH_NAME ||| echo "$NO_SUCH_NAME" output: hint: Section 8.1. The printing command echo, then a dollar sign and the name NO_SUCH_NAME, which nothing was ever stored under.
An empty line. An unset name expands to nothing at all, and the shell reports no error. That is why a misspelled name in a command quietly does the wrong thing instead of stopping.
Every program the shell starts receives a list of names and values called the environment. It is separate from the variables the shell holds for itself. env prints the environment of the shell you are typing in. It takes no arguments, so it answers the same wherever you stand:
prompt: admin@server1:~$ answer: env output: SHELL=/bin/bash PWD=/home/admin LOGNAME=admin HOME=/home/admin LANG=en_US.UTF-8 TERM=xterm-256color ... hint: Section 8.2. Three letters, no options and no arguments: the command that prints the whole environment.
One NAME=value pair per line. Six of them are quoted above and the lone ... line stands for the rest. That rest is longer, and it differs from machine to machine, because a login builds the environment from how you signed in.
SHELL is the program that reads your commands. HOME is your home directory. PWD is the directory you are standing in, and LOGNAME is your account name. Further down the list, below the cut, USER holds that name as well.
Note what is NOT here: SITE_NAME, which you set two exercises ago. Section 8.4 explains why.
printenv prints the value of one environment variable, with no name and no equals sign around it. Ask it for HOME:
prompt: admin@server1:~$ answer: printenv HOME output: /home/admin hint: Section 8.3. The environment-reading command, then the variable name HOME in capital letters.
The path of your home directory, the same value the tilde stands for in Section 8.16. echo $HOME prints the same string; printenv is the tool that reads the environment and nothing else.
Ask the environment for USER, the name of your account:
prompt: admin@server1:~$ answer: printenv USER output: admin hint: Section 8.3. The same environment-reading command, then the variable name USER.
admin, the account you are logged in as. Chapter 1 got this from the whoami program, which asks the system who owns the session. printenv reads what the login put in the environment.
Ask the environment for SITE_NAME, the name you set in Section 8.1:
prompt: admin@server1:~$ answer: printenv SITE_NAME output: hint: Section 8.3. The same environment-reading command, then the name you set in Section 8.1, SITE_NAME.
Nothing at all. The shell holds SITE_NAME, and echo $SITE_NAME still prints northwind, but the environment does not hold it. A plain assignment stores a value in the shell and stops there. That difference is the whole of Section 8.4.
When you run a program, the shell starts a second process, which is one running program with a memory of its own, and hands it a copy of the environment. Whatever the shell keeps to itself stays behind. The program deploy-check makes the boundary visible: it prints the value of DEPLOY_TARGET it received, and appends the same fact to a file.
Set the variable first. DEPLOY_TARGET gets the value staging:
prompt: admin@server1:~$ answer: DEPLOY_TARGET=staging ||| DEPLOY_TARGET="staging" ||| DEPLOY_TARGET='staging' output: hint: Section 8.1. The name DEPLOY_TARGET, the equals sign, the value staging, no spaces around it.
Silent, as every assignment is. The shell now holds DEPLOY_TARGET with the value staging.
You are in your home directory. Run the program by its path, tools/bin/deploy-check, and read what it received:
prompt: admin@server1:~$ answer: tools/bin/deploy-check ||| ~/tools/bin/deploy-check ||| ./tools/bin/deploy-check ||| /home/admin/tools/bin/deploy-check output: deploy-check: DEPLOY_TARGET is not set in my environment hint: Section 8.4. The path of the program, tools/bin/deploy-check (or ~/tools/bin/deploy-check from anywhere). A path with a slash in it runs the file directly.
The program says the variable is not set, and it is right. Its process received a copy of the environment, and DEPLOY_TARGET was never in it. Your shell still holds the value; the program simply cannot see it. Every program you will ever run works this way.
export marks a name so that its value is copied into the environment of every program the shell starts from now on. You export the NAME, without a dollar sign, because you are naming the variable rather than using its value. Export DEPLOY_TARGET:
prompt: admin@server1:~$ answer: export DEPLOY_TARGET output: hint: Section 8.5. The word export, then the name DEPLOY_TARGET, with no dollar sign and no value.
Silent. The variable already had its value; export changed nothing about it except who gets to see it. export DEPLOY_TARGET=staging does both jobs in one line and is the form you will meet most often in real work.
You are in your home directory. Run tools/bin/deploy-check again, unchanged:
prompt: admin@server1:~$ answer: tools/bin/deploy-check ||| ~/tools/bin/deploy-check ||| ./tools/bin/deploy-check ||| /home/admin/tools/bin/deploy-check output: deploy-check: DEPLOY_TARGET=staging hint: Section 8.4. The same path as Exercise 8-11: tools/bin/deploy-check (or ~/tools/bin/deploy-check from anywhere).
The same command, the same program, and a different answer, because one word changed what the shell hands out. This is how a program is configured on a server: not by editing the program, but by putting a value in the environment it will be started with.
Ask the environment for DEPLOY_TARGET now that it is exported:
prompt: admin@server1:~$ answer: printenv DEPLOY_TARGET output: staging hint: Section 8.3. The environment-reading command, then the name you exported, DEPLOY_TARGET.
The value, where Exercise 8-9 printed nothing for SITE_NAME. printenv reads the environment, so a name appears there only after it is exported.
unset removes a name from the shell, and from the environment if it was exported. Like export, it takes the name without a dollar sign. Remove DEPLOY_TARGET:
prompt: admin@server1:~$ answer: unset DEPLOY_TARGET output: hint: Section 8.6. The word unset, then the name DEPLOY_TARGET, with no dollar sign.
Silent. The name is gone from this shell. echo $DEPLOY_TARGET now prints an empty line, the same as any name that was never set.
You are in your home directory. Run tools/bin/deploy-check a third time:
prompt: admin@server1:~$ answer: tools/bin/deploy-check ||| ~/tools/bin/deploy-check ||| ./tools/bin/deploy-check ||| /home/admin/tools/bin/deploy-check output: deploy-check: DEPLOY_TARGET is not set in my environment hint: Section 8.4. The same path as Exercises 8-11 and 8-13: tools/bin/deploy-check (or ~/tools/bin/deploy-check from anywhere).
Back to the first answer. Nothing about the program changed at any point; only what the shell handed it did.
Each run appended one line to a file, so the three runs are on record. You are in your home directory. Read tools/receipts/deploy-check.log:
prompt: admin@server1:~$ answer: cat tools/receipts/deploy-check.log ||| cat ~/tools/receipts/deploy-check.log output: DEPLOY_TARGET=unset DEPLOY_TARGET=staging DEPLOY_TARGET=unset hint: Section 5.1. The printing command, then the path tools/receipts/deploy-check.log (or ~/tools/receipts/deploy-check.log from anywhere).
Three lines, in the order you ran them: before the export, after it, and after the unset. The middle line could only be written by a process that really received the value, which is why the chapter lab grades this experiment from the file rather than from what you typed.
When a bare name means an external program rather than an alias, function or builtin, Bash has to find its file. It looks in the directories of PATH from left to right and runs the first executable match. The directories are separated by colons. backup-status is such a program on this server. Print the value of PATH:
prompt: admin@server1:~$ answer: echo $PATH ||| echo "$PATH" ||| printenv PATH output: /usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games hint: Section 8.1. The printing command echo, then a dollar sign and the name PATH in capital letters. printenv PATH prints the same value.
Five directories. /usr/bin holds most of the programs on a Debian system, and /usr/local/bin is where a machine owner puts programs that are not part of any package. Your own tools directory is not on the list.
The two programs in ~/tools/bin live in a directory that is not on that list. Type the bare name backup-status, with no path at all, and watch the search fail:
prompt: admin@server1:~$ answer: backup-status output: bash: backup-status: command not found hint: Section 8.7. The name backup-status on its own, with no path and no slash.
The message starts with the name of the shell, because the shell is reporting: it looked in all five directories of PATH and found no file called backup-status. The program exists; the shell was not told where to look.
A word with a slash in it is not
Practice Chapter 8, Part 1. Bash Shell Environment: variables, env, export, PATH, which and type in a real Linux terminal at The Linux Camp. Progress is verified automatically as you type commands on the machine.