411 REFERENCE · Wikibooks
Guide to Unix/Explanations/Environment
Reference material by Wikibooks contributors. This retains its source identity and is not 411 news reporting.
Environment Variables
Every program inherits "environment variables" from the program that started it. This means that each program has the same environment variables as the program that started it. The environment of a program can change, in which case other programs later started by that program also have a changed environment.
The environment variable called SHELL contains the name of the default shell. (You might be running some other shell, but the default shell is always in "SHELL".) The "SHELL" environment variable is set during login and inherited by the shell. When we started the "printenv" program, it inherited the entire environment, including "SHELL", and showed "SHELL" to us.
Using printenv with no arguments gives the entire environment:
$ printenv
...
SHELL=/bin/bash
...
See ../../Environment Variables/ to learn about specific variables.
Process limits
Each process has limits on the following.
Limits are inherited from the parent process. The normal way to set limits is to change the limits of some shell, and then start the program from that shell. Each shell provides a different way to set limits: Bourne shell uses the ulimit command while C shell uses limit.
The following is the list of available limits, given with their C shell name ("Bourne shell option"). We need the name ("option") to show or change the limit in the C ("Bourne") shell.
* cputime ("-t SECONDS")
* filesize ("-f BLOCKS")
* datasize ("-n KILOBYTES")
* stacksize ("-s KILOBYTES")
* coredumpsize ("-c BLOCKS") can also accept "unlimited" instead of BLOCKS
* memoryuse ("-m KILOBYTES")
* memorylocked ("-l KILOBYTES")
* maxproc ("-p COUNT")
* openfiles ("-n COUNT")
Explain how to use "limit" or "ulimit -S" to set limits. Explain the danger of reducing hard limits. Explain what these limits do or refer to another chapter of this book. For example, core dump size might be covered in a chapter on crashing.
Scenario: A program crashes with a message about "resource temporarily unavailable" or "too many open files".
Raise the limits in the shell from which you start the program. For example, to allow 512 open files per process:
Bourne shell
$ ulimit -Sn 512
C shell
$ limit openfiles 512
Shell Parameters
The shell's environment can be accessed through its parameters. There are three types of parameter:
named parameters
positional parameters
special parameters
Named parameters are known as environment variables. Some of these variables are
initialised when the user logs in and which the shell uses to determines its
behavior. The shell also maintains certain variables throughout the login session,
some of which are defined by the shell and are given special meaning, while
others can be user defined.
Positional parameters enable reference to arguments supplied to shell scripts.
The shell communicates process information through special parameters. Special
parameters can be accessed by the user, but unlike named and positional parameters,
they can only be set by the shell itself.
Environment variables are named parameters. Variables are assigned using the
command line statement in the form name=value. Variable names
are alphanumeric strings, they can contain numeric characters or underscores but
must begin with an alphabetic character. Four examples are presented below:
$ n=2
$ n2=4
$ N=3
$ MYNAME=alan
$ greeting="hello world"
Note that Unix is case sensitive, so the variable "name" is not the same
as "Name". A variable can be expanded (evaluated) by preceding the variable
name with a $, for example:
$ echo $greeting
hello world
Variables can be concatenated:
$ day=21 month=01 year=2006
$ echo $day$month$year
21012006
However, there can be problem with concatenating literals with variables, using
variables for the day and month but hard-coding the year gives
$ echo $day$month2006
05
The problem is that the shell cannot distinguish between the variable name
month and the literal value 2006. It therefore tries to expand a variable
with the variable name month2006, which is not set.
This can be rectified by surrounding the variables names with braces {},
for example:
$ echo ${day}${month}2006
05112006
Although the braces are not always necessary it is good practice to use
them when evaluating variables.
Quoting
Consider the following example, where the variable expansion phrase is enclosed
in double quotes:
$ echo "echo $greeting", displays $greeting
echo hello world, displays hello world
Variables enclosed in double quotes are still expanded by the shell. To prevent
expansion, variables should be enclosed in single quotes. To display the string
"echo $greeting" verbatim, use the statement:
$ echo ’echo $greeting’, displays $greeting
echo $greeting, displays hello world
More Parameter Expansion
The shell supports quite sophisticated parameter expansion. The construct
NAME-literal expands to the value of the parameter if it is set, otherwise
it expands to literal value. For example:
$ echo ${OPSYS-unix}
unix
The variable OPSYS has not been assigned a value so the result of the command is to
echo the string "unix" to the screen. If the variable is assigned a value:
$ OPSYS=Linux
$ echo ${OPSYS-unix}
Linux
If the variable is set to NULL, a blank link is displayed rather than the literal value:
$ OPSYS=""
$ echo ${OPSYS-unix}
(blank line)
However, if we use name:-literal, the variable evaluates to the
literal rather than its NULL value:
$ echo ${OPSYS:-unix}
unix
Using the construct name=literal the parameter is assigned the value of the
literal if it has not been set. Suppose that the variable DAY has not been
previously set:
$ echo $DAY
(blank line)
then the command line below will assign the string "tuesday" to the variable DAY
and echo it to screen:
$ echo ${DAY=tuesday}
tuesday
The variable DAY has now been set, so if the command line is repeated
with a different literal value its value remains unchanged
$ echo ${DAY=wednesday}
tuesday
If DAY is assigned a NULL value, the following parameter expansion results
in a NULL value being returned:
$ DAY=""
$ echo ${DAY=tuesday}
(blank line)
An associated expansion construct is name-value, which only returns the literal
if the parameter is unassigned, it returns the assigned value even if the value is
NULL.
Using the expansion construct name:=literal the parameter is assigned the value of
the literal if it has not been set. For example:
$ echo $DISTRO # DISTRO not assigned
echo ${DISTRO:=ubuntu}
ubuntu
Also, the literal is assigned if the parameter is NULL, the expansion is therefore:
$ DISTRO=""
$ echo ${DISTO:=ubuntu}
ubuntu
If the parameter is set to a non-NULL value then the literal is not assigned:
$ echo ${DISTRO:=debian}
ubuntu
The expansion construct name=value is similar to name:=value,
however the literal is only assigned if the parameter was formerly
unassigned. The literal is not assigned if the parameter value is NULL.
Using the name:?messagestring construct, an error message can be displayed if
parameter is unset or NULL:
$ echo ${DISTVER:?distribution version unknown}
-bash: DISTVER: distribution version unknown
$ DISTVER=""
$ echo ${DISTVER:?distribution version unknown}
-bash: DISTVER: distribution version unknown
The error message is not displayed if the parameter is assigned a non-NULL
value:
$ DISTVER="hoary hedgehog"
$ echo ${DISTVER:?distribution version not set}
hoary hedgehog
Likewise, there is an associated construct name?messagestring which
returns the message string only if the parameter is unset.
Parameters can be expanded to substrings of their value. The expansion constructs
name%pattern and name%%pattern expand to the largest and smallest substrings
to the left of the pattern respectively. For example, set the variable TIME:
$ TIME=10:29:39
The command line below displays only the hour value:
$ echo the hours is ${TIME%%:*}
the hours is 10
whereas the following the command line display the hours and minutes
$ echo the hour and minutes are ${TIME%:*}
the hour and minutes are 10:29
The constructs name#pattern and name##pattern expand to the
largest and smallest substring to the right of the pattern respectively.
So the command line below expands to the seconds:
$ echo the seconds are ${TIME##*:}
the seconds are 39
The minutes and seconds can be extracted with:
$ echo the minutes and seconds are ${TIME#*:}
the minutes and seconds are 29:39
The length of a string can be evaluated with the #name construct. For example:
$ echo ${#TIME}
8
Arrays
Variables can be one-dimensional arrays and are referenced in the form name[index].
Arrays can be assigned using the name=(value1 value2 ... ) construct,
so for example:
DATE=($(date))
$(command) expands to the result of command on the command line.
So DATE is assigned output of the command date, where each space separated
string is assigned to an element in the array. So if we only want the year, then we
expand the sixth element of the array:
$ echo ${DATE[5]}
2006
We can change, say the time information with the assignment:
DATE[3]=19:16:00
$ echo ${DATE[*]}
Wed May 17 19:16:00 NZST 2006
If we need to count the number or assigned array elements, then we use the
expression:
$ echo ${#DATE[*]}
6
Positional Parameters
Positional parameters provide a means of accessing arguments to shell scripts.
They are referenced by the numbers 1,2,3,... etc., where the numbers reflect the
order in which the arguments appeared on the command line (from left to right).
They cannot be assigned in the same way as named parameters, that is you cannot
do this: 1=hello. Positional parameters are assigned when a shell script is
invoked with command line arguments. They can, however, be assigned with the set
command:
$ set $(uname -rmv)
This causes the space separated strings from the output of uname -rmv to be
assigned to the positional parameters. The statement below shows the values of all
positional parameters that were assigned:
$ echo ${*}
2.6.14.4 #1 SMP Thu Dec 22 01:04:35 NZDT 2005 x86_64
And this statement gives the number of assigned positional parameters
(equivalent to argc in C and C++):
$ echo $#
10
If we specifically want the kernel version then we expand positional parameter 1:
$ echo $1
2.6.14.4
Double digit positional parameters need to be enclosed in braces. So
if we want the architecture of the machine, we cannot do this:
$ echo $10
2.6.14.40
The expression above is expanded to positional parameter 1 followed by a literal 0.
The correct way is:
$ echo ${10}
x86_64
Finally, positional parameter 0 is set to the name of the current process
(equivalent to argv[0], which in this case should be the name of the shell
command itself:
$ echo $0
-bash
Special Parameters
Special parameters can be accessed by the user but can only be assigned (directly)
by the shell - typically in response to some event. We have already encountered the
parameters * and # which are set when a command is invoked with arguments (or
the set command is used).
The ? parameter expands to the return code of the process that last exited. If we
enclose a command line statement in brackets it is executed in a subshell. The
command line below forks a new shell and immediately issues exit. The argument
following exit is the value of the subshell’s return code (255 in this case):
$ (exit 255)
The return code of the child process can be accessed by the parent by the expansion
of ?. If a process terminated normally then the return code is likely to be zero. If
however, the process terminated due to some error condition, it is useful if process
sets a non-zero return code so that the parent can determine the reason for the
termination:
$ echo $?
255
The process ID of the shell is stored in $. The ps command shows that the process
ID of my shell is 10005, which is what is given by the expansion of $:
$ ps | grep bash
10005 pts/77 00:00:00 bash
$ echo $$
10005
The ! parameter evaluates to the process ID of the last background process. To
demonstrate, the statement below invokes the sleep command (which sleeps for 60
seconds) in the background:
$ sleep 60 &
[1] 24835
The process ID of the background process is displayed on the screen (in this case
24835). Expansion of the ! parameter confirms this:
$ echo $!
24835
Exporting Parameters
Processes do not inherit environment variables from their parent unless the
variables are exported. The command line below displays the process ID of
the current process followed by the value of greeting (which was assigned
earlier):
$ echo ${greeting?parameter not set}
hello world
If we start a new shell and issue the command again we see that greeting is not set
in the child process:
$ bash
$ echo ${greeting?parameter not set}
bash: greeting: parameter not set
Terminate the current shell process and return to the parent, then export greeting:
$ exit
$ export greeting
You can confirm that a variable has the export attribute set by typing:
$ export | grep greeting
declare -x greeting="hello world"
Now start a new shell again and evaluate greeting:
$ bash
$ echo ${greeting?parameter not set}
hello world
The new shell has inherited the variable greeting and can evaluate it.
A variable retains its export attribute (until the shell is terminated) even
if it is reassigned, but it can be removed using the typeset command:
$ typeset +x greeting
$ export | grep greeting # yields no result
In Bash (and Kornshell, but not Bourne shell), a variable can be assigned and
exported in one command line statement:
$ export greeting="hi there"
$ export | grep greeting
declare -x greeting="hi there"
deep