Environment variables
Set variables
Local variables
Variable substitutions
nonovar
How variables are stored
Arrays
nullwords
See also
As with any conventional programming language, the shell provides a facility for storing values in variables.
Some of the variables are part of the environment, passed along to any child process. Many of the environment variables will have been created just this way, e.g., set to a value passed along when you start the C shell from a shortcut on your desktop. To list those currently defined, use the setenv command.
Variable names are case-preserving but case-insensitive on Windows. They can be of arbitrary length. A name must start with an upper- or lower-case alphabetic character or underscore_ or at-sign @; remaining characters may any of these or decimal digits. Many of the environmental variables have specific meanings. For example, the PATH variable tells where to look for executable files, etc. Details describing the meaning of each variable are given in the language reference section.
The setenv command can also be used to create a new environmental variable or alter or display the value of an existing one:
If the list of words being assigned to the variable includes any special tokens, it’s often useful to use the parenthesized variant of setenv. In this example, the > would have been confused as an i/o redirection if it weren’t inside parenthesis. Notice that the parenthesis are stripped off before the assignment is made.
Even though the special meaning is lost, text inside the parenthesis is still broken down into words, as shown in this example:
(To avoid having the text broken up into words, use single or double quotes around the string instead.)
Set variables do not get passed to a child process but are shared among all threads. To get a list of those currently defined, use the set command:
Some of the set variables are linked to the environmental variables: you change one, and the other changes too. For example, path contains the same information as PATH but, because it’s been parsed into individual words, it’s often a bit more useful.
On Windows, this linkage can pose a bit of a problem. Since the convention is that environmental variables are supposed to be case-insensitive, there clearly is a conflict between, for example, the PATH environmental and path set variables. The C shell resolves this by making the set, unset and @ statements case-sensitive (so you can still create set variables that differ from environmental variables only by case) but the setenv and unsetenv and $var and other variable references first try case-sensitive, then case insensitive variable lookups.
Many of the set variables are predefined by the shell to control various aspects of how errors are handled, etc. In some cases, each thread maintains its own copy. For example, it wouldn’t do to insist that all threads must use the same value for the cwd (current working directory) variable! The rest of the variables, including any defined by the user, are shared among all threads: if one thread changes a value, all the other threads see the change immediately. As we’ll see later, this has some implications when spawning background activities.
In other respects, set works just like setenv:
Once a variable has been created as either a set or an environmental variable, it stays that way: to change it from set to environmental, you must first unset it, then redefine it.
We just mentioned that not all the predefined set variables are shared. Individual threads get their own private copies of some because to do otherwise wouldn’t be sensible. Sometimes you need the same sort of control over the variables you create. You don’t want to share a variable with other threads or even with commands outside a very narrow context.
You accomplish this making the variable local, which means it’s hidden from outer control blocks or other threads. Local variables are really important, as we’ll see later, for recursive procedures or for procedures you want to use from multiple threads. To define a variable as local, use the local statement, which accepts a list, separated with commas, of all the variable names you want to be local. When a new local variable is created, its initial value is always null (zero words), even if there was a previous definition. Here you can see how the variable i is redefined inside the nested statements but once you exit from the nest, the old value of i is again visible:
When you spawn a child thread, e.g., as a background activity or as the second or following stage of a pipeline, it gets copies of all your local variables, snapshotted at the time it’s spawned. If either the parent or the child later changes to the value of any those local variables, they affect only its own copy.
The simplest way to use a variable is in a simple substitution, where a dollar sign is used to indicate that a variable name follows, similar to using a % in cmd.exe. The value is substituted in and the statement is evaluated.
Text surrounding the variable reference is generally just pasted around the value that’s substituted in:
If the surrounding text would be confused as part of the variable name, it’s necessary to insulate the variable reference with braces. For example:
If you try to reference a variable, procedure or an alias and it doesn’t exist, it’s considered an error unless you set the nonovar variable to indicate how you want the situation treated.
Each variable is kept internally as a list (an array) of objects. An individual object can be a (possibly null) character string, a 32-bit integer or a 64-bit floating point value. Generally speaking, it’s not necessary to worry too much about how a specific object is represented, though, since the shell automatically does any necessary conversions to allow a value to be used sensibly in any given context.
Notice, however, that even though the value of an environmental variable may be a list, it is always rendered as a simple character string when it’s passed to a child process. Here’s an example using the $# notation to ask how many words are in a variable’s value:
In this example, zork holds five words: this, is, the, zork and variable. But when we start up a new child process running the shell, the child process sees zork as holding only a single word: this is the zork variable.
Here’s another example where we assign a floating point value to an environmental variable. In the current process, the exact binary floating representation is used. When it’s passed to a child process, the value is first converted to a character string, losing some of the precision. This example also introduces the calc statement which evaluates an expression and prints the value. In an expression, a variable name is recognized even without a $ to introduce it; in fact, that’s the preferable way to do it. If you use a $-style variable substitution, the shell pastes in a character-string representation, again losing precision. Also, the full range of C language expression operators is available.
Variables can hold a list of values indexed as an array. As with the C language, arrays are indexed, counting from zero, with an expression inside [...] brackets. The expression can be arbitrarily complex, but it must evaluate to an exact integer value. The next example shows how a floating point value can be used, but if it’s not precisely an integer, an error results. The precision variable controls the number of digits past the decimal point used when displaying a floating point value; the maximum is 17 digits.
An array cannot be sparse; i.e., before you can create an element 5, element 4 must exist and so on. But it’s perfectly legal to keep adding new elements onto the end of array as long as the new element is the zero-th element or only one past whatever’s currently the last element. Here’s an example using a for loop. The @ statement is like calc except it doesn’t print anything.
When you index an array, if the element you specify doesn’t exist, that’s normally considered an error. For example, x contains 5 words; trying to reference a sixth fails:
The use of the -- option and of double quotes was important: -- told echo that it had reached the end of any options, allowing it to print something that began with a minus sign. The quotes were used, in the first case, to turn off recognition of redirection characters > and < but still get the variable substitution. The second time, it was to make the string, does not work a single word. (If you try leaving off the quotes or not using --, you’ll see that the error messages are what you’d expect.)
The @, calc, set, setenv and local statements
Variable substitution syntax
Substitution modifiers
Predefined environment variables
Predefined shared variables
Predefined inherited variables
Predefined initialized variables
Combined list of predefined variables
Quoting
Order of evaluation
Tutorial: Expressions