The shell environment

INPUT · Slides

Containers for values (variables)

01 / 06

You are already using variables

This chapter is about what happens to what you type before it reaches the command. Start with variables.

You are using them already.

~ $ cd ~~ $ echo $HOME/home/learner

$HOME is a container with the place of your room in it. ~ points at the same thing.

Others are in there from the start.

NameContents
HOMEyour own room
USERyour own name
PATHwhere commands are looked for
PWDwhere you are
SHELLthe shell you are using

The important part is that it is not the command that replaces $HOME.

echo $HOME  | the shell rewrites it firstecho /home/learner  | this is what reaches echo

echo has never even seen the characters $HOME. The shell is interpreting. That view is the whole of chapter 10.

02 / 06

Making your own

You can make containers yourself. This is all there is to it.

~ $ MYNAME=taro~ $ echo $MYNAMEtaro

Putting something in takes no $; taking it out takes a $. A little odd.

MYNAME=taro     put (no $)echo $MYNAME    use (with $)

And you must not put spaces in.

~ $ MYNAME = taro-sh: MYNAME: not found

Can you see why that is an error? The shell reads by splitting on spaces, so it sees this.

run the command named MYNAME, with = and taro as arguments

There is no such command, hence not found. The rule that a space is a separator is at work here too.

To put a space in the contents, wrap it in quotes.

MESSAGE="good morning"

The fine detail of quotes gets plenty of room later in this chapter.

03 / 06

It does not pass to the child

This is the most important thing today.

~ $ MYNAME=taro~ $ sh -c 'echo [$MYNAME]'[]

Empty. sh -c means start one new shell and type it in there — a child process, in other words.

Variables do not usually pass to a child.

your shell  MYNAME=taro (a note kept only here)  \- the child shell       no MYNAME

To pass it, add export.

~ $ export MYNAME=taro~ $ sh -c 'echo $MYNAME'taro

The two have names.

What it is calledWhat differs
a shell variableonly inside that shell
an environment variable (exported)passes to the children it starts

They are kept apart because passing along things that need not be passed makes it heavy. Temporary containers for a bit of work are tidier kept in.

And do not forget: export does not send it back to the parent. It only passes downwards.

04 / 06

See the environment with env

What is exported right now shows with env.

~ $ envHOME=/home/learnerPATH=/bin:/sbin:/usr/bin:/usr/sbinUSER=learner...

It lines up as name=value, so grep narrows it down.

env | grep PATH

env has another use: passing a variable for that one time.

~ $ env B=jiro sh -c 'echo $B'jiro~ $ echo [$B][]

It reached the child, and yet nothing is left where you are. A way of passing something once without making a mess.

To remove one there is unset.

unset MYNAME

And the way of writing a default for when there is nothing is handy to know.

~ $ echo ${MISSING:-fallback}fallback

${name:-other} means "that, if it is in there; otherwise the other". You use it a lot writing scripts: the way of writing something that does not stop when a setting is absent.

05 / 06

It expands once, and once only

Look at what happens when you put a variable into another variable.

~ $ X=1~ $ Y="X is $X"~ $ echo $YX is 1~ $ X=2~ $ echo $YX is 1        <- unchanged!

Y is not "a device that goes and looks at $X". It is plain characters, copied from the contents at the time.

the moment you write Y="X is $X"  | the shell replaces $X with 1Y becomes the characters "X is 1"

That is the rule that expansion happens once, at the moment you write it. Unlike a formula in a spreadsheet, so take care.

Some things do keep changing.

~ $ cd /tmp/tmp $ echo $PWD/tmp

The shell rewrites PWD every time you cd. You did not assign it: a variable the shell looks after.

VariableWho puts it in
PWD OLDPWDthe shell, automatically
? ($?)automatically, from the last command's result
your own variablesyou

$? takes the lead in chapter 11 — the "0 is success" one.

06 / 06

Now have a go

Here are the shapes for this lesson.

echo $HOME              see what is in a containerMYNAME=taro             make one (no spaces)export MYNAME=taro      make one that passes to childrenexport MYNAME           make it pass, afterwardsunset MYNAME            remove itenv                     list the ones that passenv B=jiro command      pass for that one timesh -c 'echo $MYNAME'    check in a child${MISSING:-fallback}    the stand-in for when there is nothing

One knack for checking. Whether it passes can only be seen from the child.

echo $MYNAME            where you are (visible even if it does not pass)sh -c 'echo $MYNAME'    in the child (empty if it does not pass)

Typing those two side by side is the shape of the practice here.

Do not forget to wrap the inside of sh -c in single quotes. With double quotes, your own shell replaces $MYNAME first and you have not checked the child at all.

sh -c 'echo $MYNAME'    the child looks at $MYNAME (the right check)sh -c "echo $MYNAME"    the child merely echoes what you replaced

That difference gets its proper turn later in this chapter. For now, remember "wrap it in singles".