Parameters and Variables
Variables
Within a shell, a shell parameter is associated with a value that is accessible to the user. There are several kinds of shell parameters. Parameters whose names consist of letters, digits, and underscores are often referred to as shell variables, or simply variables. A variable name must start with a letter or underscore, not with a number. Thus A76, MY_CAT, and ___X___ are valid variable names, whereas 69TH_STREET (starts with a digit) and MY-NAME (contains a hyphen) are not.
User-created variables
Shell variables that you name and assign values to are user-created variables. You can change the values of user-created variables at any time, or you can make them readonly so that their values cannot be changed. You can also make user-created variables global. A global variable (also called an environment variable) is available to all shells and other programs you fork from the original shell. One naming convention is to use only uppercase letters for global variables and to use mixed-case or lowercase letters for other variables. Refer to "Locality of Variables" on page 560 for more information on global variables.
To assign a value to a variable in the Bourne Again Shell, use the following syntax:
VARIABLE=value
There can be no whitespace on either side of the equal sign (=). An example assignment follows:
$ myvar=abc
Under the TC Shell the assignment must be preceded by the word set and the SPACEs on either side of the equal sign are optional:
$ set myvar = abc
The Bourne Again Shell permits you to put variable assignments on a command line. These assignments are local to the command shell—that is, they apply to the command only. The my_script shell script displays the value of TEMPDIR. The following command runs my_script with TEMPDIR set to /Users/sam/temp. The echo builtin shows that the interactive shell has no value for TEMPDIR after running my_script. If TEMPDIR had been set in the interactive shell, running my_script in this manner would have had no effect on its value.
$ cat my_script echo $TEMPDIR $ TEMPDIR=/Users/sam/temp my_script /Users/sam/temp $ echo $TEMPDIR $
Keyword variables
Keyword shell variables (or simply keyword variables) have special meaning to the shell and usually have short, mnemonic names. When you start a shell (by logging in, for example), the shell inherits several keyword variables from the environment. Among these variables are HOME, which identifies your home directory, and PATH, which determines which directories the shell searches and in what order to locate commands that you give the shell. The shell creates and initializes (with default values) other keyword variables when you start it. Still other variables do not exist until you set them.
You can change the values of most of the keyword shell variables at any time but it is usually not necessary to change the values of keyword variables initialized in the /etc/profile or /etc/csh.cshrc systemwide startup files. If you need to change the value of a bash keyword variable, do so in one of your startup files (for bash see page 259; for tcsh see page 340). Just as you can make user-created variables global, so you can make keyword variables global; this is usually done automatically in the startup files. You can also make a keyword variable readonly.
Positional parameters Special parameters
The names of one group of parameters do not resemble variable names. Most of these parameters have one-character names (for example, 1, ?, and #) and are referenced (as are all variables) by preceding the name with a dollar sign ($1, $?, and $#). The values of these parameters reflect different aspects of your ongoing interaction with the shell.
Whenever you give a command, each argument on the command line becomes the value of a positional parameter. Positional parameters (page 564) enable you to access command line arguments, a capability that you will often require when you write shell scripts. The set builtin (page 568) enables you to assign values to positional parameters.
Other frequently needed shell script values, such as the name of the last command executed, the number of command line arguments, and the status of the most recently executed command, are available as special parameters. You cannot assign values to special parameters.
User-Created Variables
The first line in the following example declares the variable named person and initializes it with the value alex (use set person = alex in tcsh):
$ person=alex $ echo person person $ echo $person alex
Because the echo builtin copies its arguments to standard output, you can use it to display the values of variables. The second line of the preceding example shows that person does not represent alex. Instead, the string person is echoed as person. The shell substitutes the value of a variable only when you precede the name of the variable with a dollar sign ($). The command echo $person displays the value of the variable person; it does not display $person because the shell does not pass $person to echo as an argument. Because of the leading $, the shell recognizes that $person is the name of a variable, substitutes the value of the variable, and passes that value to echo. The echo builtin displays the value of the variable—not its name—never knowing that you called it with a variable.
Quoting the $
You can prevent the shell from substituting the value of a variable by quoting the leading $. Double quotation marks do not prevent the substitution; single quotation marks or a backslash (\) do.
$ echo $person alex $ echo "$person" alex $ echo '$person' $person $ echo \$person $person
SPACEs
Because they do not prevent variable substitution but do turn off the special meanings of most other characters, double quotation marks are useful when you assign values to variables and when you use those values. To assign a value that contains SPACEs or TABs to a variable, use double quotation marks around the value. Although double quotation marks are not required in all cases, using them is a good habit.
$ person="alex and jenny" $ echo $person alex and jenny $ person=alex and jenny bash: and: command not found
When you reference a variable that contains TABs or multiple adjacent SPACEs, you need to use quotation marks to preserve the spacing. If you do not quote the variable, the shell collapses each string of blank characters into a single SPACE before passing the variable to the utility:
$ person="alex and jenny" $ echo $person alex and jenny $ echo "$person" alex and jenny
When you execute a command with a variable as an argument, the shell replaces the name of the variable with the value of the variable and passes that value to the program being executed. If the value of the variable contains a special character, such as * or ?, the shell may expand that variable.
Pathname expansion in assignments
The first line in the following sequence of commands assigns the string alex * to the variable memo. The Bourne Again Shell does not expand the string because bash does not perform pathname expansion (page 133) when assigning a value to a variable. All shells process a command line in a specific order. Within this order bash (but not tcsh) expands variables before it interprets commands. In the following echo command line, the double quotation marks quote the asterisk (*) in the expanded value of $memo and prevent bash from performing pathname expansion on the expanded memo variable before passing its value to the echo command:
$ memo=alex* $ echo "$memo" alex*
All shells interpret special characters as special when you reference a variable that contains an unquoted special character. In the following example, the shell expands the value of the memo variable because it is not quoted:
$ ls alex.report alex.summary $ echo $memo alex.report alex.summary
Here the shell expands $memo to alex*, expands alex* to alex.report and alex.summary, and passes these two values to echo.
unset: Removes a Variable
Unless you remove a variable, it exists as long as the shell in which it was created exists. To remove the value of a variable but not the variable itself, set the value to null (use set person = in tcsh):
$ person= $ echo $person $
You can remove a variable with the unset builtin. To remove the variable person, give the following command:
$ unset person
Variable Attributes
This section discusses attributes and explains how to assign them to variables.
readonly: Makes the Value of a Variable Permanent
You can use the readonly builtin (not in tcsh) to ensure that the value of a variable cannot be changed. The next example declares the variable person to be readonly. You must assign a value to a variable before you declare it to be readonly; you cannot change its value after the declaration. When you attempt to unset or change the value of a readonly variable, the shell displays an error message:
$ person=jenny $ echo $person jenny $ readonly person $ person=helen bash: person: readonly variable
If you use the readonly builtin without an argument, it displays a list of all readonly shell variables. This list includes keyword variables that are automatically set as readonly as well as keyword or user-created variables that you have declared as readonly. See "Listing variable attributes" on page 283 for an example (readonly and declare -r produce the same output).
declare and typeset: Assign Attributes to Variables
The declare and typeset builtins (two names for the same command, neither of which is available in tcsh) set attributes and values for shell variables. Table 8-3 lists five of these attributes.
Table 8-3. Variable attributes (typeset or declare)
Attribute |
Meaning |
-a |
Declares a variable as an array (page 558) |
-f |
Declares a variable to be a function name (page 314) |
-i |
Declares a variable to be of type integer (page 284) |
-r |
Makes a variable readonly; also readonly (page 282) |
-x |
Exports a variable (makes it global); also export (page 560) |
The following commands declare several variables and set some attributes. The first line declares person1 and assigns it a value of alex. This command has the same effect with or without the word declare.
$ declare person1=alex $ declare -r person2=jenny $ declare -rx person3=helen $ declare -x person4
The readonly and export builtins are synonyms for the commands declare -r and declare -x, respectively. It is legal to declare a variable without assigning a value to it, as the preceding declaration of the variable person4 illustrates. This declaration makes person4 available to all subshells (makes it global). Until an assignment is made to the variable, it has a null value.
You can list the options to declare separately in any order. The following is equivalent to the preceding declaration of person3:
$ declare -x -r person3=helen
Use the + character in place of - when you want to remove an attribute from a variable. You cannot remove a readonly attribute however. After the following command is given, the variable person3 is no longer exported but it is still readonly.
$ declare +x person3
You can also use typeset instead of declare.
Listing variable attributes
Without any arguments or options, the declare builtin lists all shell variables. The same list is output when you run set (page 568) without any arguments.
If you use a declare builtin with options but no variable names as arguments, the command lists all shell variables that have the indicated attributes set. For example, the option -r with declare gives a list of all readonly shell variables. This list is the same as that produced by a readonly command without any arguments. After the declarations in the preceding example have been given, the results are as follows:
$ declare -r declare -ar BASH_VERSINFO='([0]="2" [1]="05b" [2]="0" [3]="1" ... )' declare -ir EUID="500" declare -ir PPID="936" declare -r SHELLOPTS="braceexpand:emacs:hashall:histexpand:history:..." declare -ir UID="500" declare -r person2="jenny" declare -rx person3="helen"
The first five entries are keyword variables that are automatically declared as readonly. Some of these variables are stored as integers (-i). The -a option indicates that BASH_VERSINFO is an array variable; the value of each element of the array is listed to the right of an equal sign.
Integer
By default the values of variables are stored as strings. When you perform arithmetic on a string variable, the shell converts the variable into a number, manipulates it, and then converts it back to a string. A variable with the integer attribute is stored as an integer. Assign the integer attribute as follows:
$ typeset -i COUNT
Keyword Variables
Keyword variables either are inherited or are declared and initialized by the shell when it starts. You can assign values to these variables from the command line or from a startup file. Typically you want these variables to apply to all subshells you start as well as to your login shell. For those variables not automatically exported by the shell, you must use export (bash, page 560) or setenv (tcsh, page 353) to make them available to child shells.
HOME: Your Home Directory
By default your home directory is your working directory when you log in. Your home directory is determined when you establish your account; its name is stored in the NetInfo database (page 441).
When you log in, the shell inherits the pathname of your home directory and assigns it to the variable HOME. When you give a cd command without an argument, cd makes the directory whose name is stored in HOME the working directory:
$ pwd /Users/alex/laptop $ echo $HOME /Users/alex $ cd $ pwd /Users/alex
This example shows the value of the HOME variable and the effect of the cd builtin. After you execute cd without an argument, the pathname of the working directory is the same as the value of HOME: your home directory.
Tilde (~)
The shell uses the value of HOME to expand pathnames that use the shorthand tilde (~) notation (page 78) to denote a user's home directory. The following example uses echo to display the value of this shortcut and then uses ls to list the files in Alex's laptop directory, which is a subdirectory of his home directory:
$ echo ~ /Users/alex $ ls ~/laptop tester count lineup
PATH: Where the Shell Looks for Programs
When you give the shell an absolute or relative pathname rather than a simple filename as a command, it looks in the specified directory for an executable file with the specified filename. If the file with the pathname you specified does not exist, the shell reports command not found. If the file exists as specified but you do not have execute permission for it, or in the case of a shell script you do not have read and execute permission for it, the shell reports Permission denied.
If you give a simple filename as a command, the shell searches through certain directories for the program you want to execute. It looks in several directories for a file that has the same name as the command and that you have execute permission for (a compiled program) or read and execute permission for (a shell script). The PATH shell variable controls this search.
The default value of PATH is determined when bash or tcsh is compiled. It is not set in a startup file, although it may be modified there. Normally the default specifies that the shell search several system directories used to hold common commands and then search the working directory. These system directories include /bin and /usr/bin and other directories appropriate to the local system. When you give a command, if the shell does not find the executable—and, in the case of a shell script, readable—file named by the command in any of the directories listed in PATH, the shell generates one of the aforementioned error messages.
Working directory
The PATH variable specifies the directories in the order the shell should search them. Each directory must be separated from the next by a colon. The following command sets PATH so that a search for an executable file starts with the /usr/local/bin directory. If it does not find the file in this directory, the shell first looks in /bin, and then in /usr/bin. If the search fails in those directories, the shell looks in the bin director, a subdirectory of the user's home directory. Finally the shell looks in the working directory. Exporting PATH makes its value accessible to subshells:
$ export PATH=/usr/local/bin:/bin:/usr/bin:~/bin:
A null value in the string indicates the working directory. In the preceding example, a null value (nothing between the colon and the end of the line) appears as the last element of the string. The working directory is represented by a leading colon (not recommended; see the following security tip), a trailing colon (as in the example), or two colons next to each other anywhere in the string. You can also represent the working directory explicitly with a period (.).
See "PATH" on page 359 for a tcsh example. Because Mac OS X stores many executable files in directories named bin (binary), users typically put their own executable files in their own ~/bin directories. If you put your own bin directory at the end of your PATH, as in the preceding example, the shell looks there for any commands that it cannot find in directories listed earlier in PATH.
If you want to add directories to PATH, you can reference the old value of the PATH variable while you are setting PATH to a new value (but see the preceding security tip). The following command adds /usr/X11R6/bin to the beginning of the current PATH and /usr/local/bin and the working directory to the end:
$ PATH=/usr/X11R6/bin:$PATH:/usr/local/bin
MAIL: Where Your Mail Is Kept
The MAIL variable (mail under tcsh) contains the pathname of the file that holds your mail (your mailbox, usually /var/mail/name , where name is your login name). If MAIL is set and MAILPATH (next) is not set, the shell informs you when mail arrives in the file specified by MAIL. In a graphical environment you can unset MAIL so that the shell does not display mail reminders in a terminal emulator window (assuming you are using a graphical mail program). Most Mac OS X systems do not use local files for incoming mail; mail is typically kept on a remote mail server instead. The MAIL variable and other mail-related shell variables do not do anything unless you have a local mail server.
The MAILPATH variable (not available under tcsh) contains a list of filenames separated by colons. If this variable is set, the shell informs you when any one of the files is modified (for example, when mail arrives). You can follow any of the filenames in the list with a question mark (?), followed by a message. The message replaces the you have mail message when you get mail while you are logged in.
The MAILCHECK variable (not available under tcsh) specifies how often, in seconds, the shell checks for new mail. The default is 60 seconds. If you set this variable to zero, the shell checks before each prompt.
PS1: User Prompt (Primary)
The default Bourne Again Shell prompt is a dollar sign ($). When you run bash as root, you may have a pound sign (#) prompt. The PS1 variable (prompt under tcsh, page 360) holds the prompt string that the shell uses to let you know that it is waiting for a command. When you change the value of PS1 or prompt, you change the appearance of your prompt.
You can customize the prompt displayed by PS1. For example, the assignment
$ PS1="[\u@\h \W \!]$ "
displays the following prompt:
[ user@host directory event ]$
where user is the username, host is the hostname up to the first period, directory is the basename of the working directory, and event is the event number of the current command.
If you are working on more than one system, it can be helpful to incorporate the system name into your prompt. For example, you might change the prompt to the name of the system you are using, followed by a colon and a SPACE (a SPACE at the end of the prompt makes the commands that you enter after the prompt easier to read):
$ PS1="$(hostname): " bravo.example.com: echo test test bravo.example.com:
Use the following command under tcsh:
tcsh $ set prompt = "`hostname` : "
The first example that follows changes the prompt to the name of the local host, a SPACE, and a dollar sign (or, if the user is running as root, a pound sign). The second example changes the prompt to the time followed by the name of the user. The third example changes the prompt to the one used in this book (a pound sign for root and a dollar sign otherwise):
$ PS1='\h \$ ' bravo $ $ PS1='\@ \u $ ' 09:44 PM alex $ $ PS1='\$ ' $
Table 8-4 describes some of the symbols you can use in PS1. For a complete list of special characters you can use in the prompt strings, open the bash man page and search for the second occurrence of PROMPTING (give the command /PROMPTING and then press n).
Table 8-4. PS1 symbols
Symbol |
Display in prompt |
\$ |
# if the user is running as root; otherwise, $ |
\w |
Pathname of the working directory |
\W |
Basename of the working directory |
\! |
Current event (history) number (page 299) |
\d |
Date in Weekday Month Date format |
\h |
Machine hostname, without the domain |
\H |
Full machine hostname, including the domain |
\u |
Username of the current user |
\@ |
Current time of day in 12-hour, AM/PM format |
\T |
Current time of day in 12-hour HH:MM:SS format |
\A |
Current time of day in 24-hour HH:MM format |
\t |
Current time of day in 24-hour HH:MM:SS format |
PS2: User Prompt (Secondary)
Prompt String 2 is a secondary prompt that the shell stores in PS2 (not under tcsh). On the first line of the next example, an unclosed quoted string follows echo. The shell assumes that the command is not finished and, on the second line, gives the default secondary prompt (>). This prompt indicates that the shell is waiting for the user to continue the command line. The shell waits until it receives the quotation mark that closes the string and then executes the command:
$ echo "demonstration of prompt string > 2" demonstration of prompt string 2 $ PS2="secondary prompt: " $ echo "this demonstrates secondary prompt: prompt string 2" this demonstrates prompt string 2
The second command changes the secondary prompt to secondary prompt: followed by a SPACE. A multiline echo demonstrates the new prompt.
PS3: Menu Prompt
PS3 holds the menu prompt for the select control structure (page 552).
PS4: Debugging Prompt
PS4 holds the bash debugging symbol (page 536).
IFS: Separates Input Fields (Word Splitting)
The IFS (Internal Field Separator) shell variable (not under tcsh) specifies the characters that you can use to separate arguments on a command line and has the default value of SPACE TAB NEWLINE. Regardless of the value of IFS, you can always use one or more SPACE or TAB characters to separate arguments on the command line, provided that these characters are not quoted or escaped. When you assign IFS character values, these characters can also separate fields but only if they undergo expansion. This type of interpretation of the command line is called word splitting.
The following example demonstrates how setting IFS can affect the interpretation of a command line:
$ a=w:x:y:z $ cat $a cat: w:x:y:z: No such file or directory $ IFS=":" $ cat $a cat: w: No such file or directory cat: x: No such file or directory cat: y: No such file or directory cat: z: No such file or directory
The first time cat is called, the shell expands the variable a, interpreting the string w:x:y:z as a single word to be used as the argument to cat. The cat utility cannot find a file named w:x:y:z and reports an error for that filename. After IFS is set to a colon (:), the shell expands the variable a into four words, each of which is an argument to cat. Now cat reports an error for four separate files: w, x, y, and z. Word splitting based on the colon (:) takes place only after the variable a is expanded.
The shell splits all expanded words on a command line according to the separating characters found in IFS. When there is no expansion, there is no splitting. Consider the following commands:
$ IFS="p" $ export VAR
Although IFS is set to p, the p on the export command line is not expanded so the word export is not split.
The next example uses variable expansion in an attempt to produce an export command:
$ IFS="p" $ aa=export $ echo $aa ex ort
This time expansion occurs so that the character p in the token export is interpreted as a separator as the preceding echo command shows. Now when you try to use the value of the aa variable to export the VAR variable, the shell parses the $aa VAR command line as ex ort VAR. The effect is that the command line starts the ex editor with two filenames: ort and VAR.
$ $aa VAR 2 files to edit "ort" [New File] Entering Ex mode. Type "visual" to go to Normal mode. :q E173: 1 more file to edit :q $
If you unset IFS, only SPACEs and TABs work as field separators.
CDPATH: Broadens the Scope of cd
The CDPATH variable (cdpath under tcsh) allows you to use a simple filename as an argument to the cd builtin to change the working directory to a directory other than a child of the working directory. If you have several directories you like to work out of, this variable can speed things up and save you the tedium of using cd with longer pathnames to switch among them.
When CDPATH or cdpath is not set and you specify a simple filename as an argument to cd, cd searches the working directory for a subdirectory with the same name as the argument. If the subdirectory does not exist, cd displays an error message. When CDPATH or cdpath is set, cd searches for an appropriately named subdirectory in the directories in the CDPATH list. If cd finds one, that directory becomes the working directory. With CDPATH or cdpath set, you can use cd and a simple filename to change the working directory to a child of any of the directories listed in CDPATH or cdpath.
The CDPATH or cdpath variable takes on the value of a colon-separated list of directory pathnames (similar to the PATH variable). It is usually set in the ~/.bash_profile (bash) or ~/.tcshrc (tcsh) startup file with a command line such as the following:
export CDPATH=$HOME:$HOME/literature
Use the following format for tcsh:
setenv cdpath $HOME\:$HOME/literature
These commands cause cd to search your home directory, the literature directory, and then the working directory when you give a cd command. If you do not include the working directory in CDPATH or cdpath, cd searches the working directory if the search of all the other directories in CDPATH or cdpath fails. If you want cd to search the working directory first (which you should never do when you are logged in as root—refer to the tip on page 286), include a null string, represented by two colons (::), as the first entry in CDPATH:
export CDPATH=::$HOME:$HOME/literature
If the argument to the cd builtin is an absolute pathname—one starting with a slash (/)—the shell does not consult CDPATH or cdpath.
Keyword Variables: A Summary
Table 8-5 lists the bash keyword variables.
Table 8-5. bash keyword variables
Variable |
Value |
BASH_ENV |
The pathname of the startup file for noninteractive shells (page 260) |
CDPATH |
The cd search path (page 290) |
COLUMNS |
The width of the display used by select (page 551) |
FCEDIT |
The name of the editor that fc uses by default (page 298) |
HISTFILE |
The pathname of the file that holds the history list (default: ~/.bash_history; page 295) |
HISTFILESIZE |
The maximum number of entries saved in HISTFILE (default: 500; page 295) |
HISTSIZE |
The maximum number of entries saved in the history list (default: 500; (page 295) |
HOME |
The pathname of the user's home directory (page 284); used as the default argument for cd and in tilde expansion (page 78) |
IFS |
Internal Field Separator (page 288); used for word splitting (page 328) |
INPUTRC |
The pathname of the Readline startup file (default: ~/.inputrc; page 308) |
LANG |
The locale category when that category is not specifically set with a LC_* variable |
LC_* |
A group of variables that specify locale categories including LC_COLLATE, LC_CTYPE, LC_MESSAGES, and LC_NUMERIC; use the locale builtin to display a complete list with values |
LINES |
The height of the display used by select (page 551) |
|
The pathname of the file that holds a user's mail (page 286) |
MAILCHECK |
How often, in seconds, bash checks for mail (page 286) |
MAILPATH |
A colon-separated list of file pathnames that bash checks for mail in (page 286) |
PATH |
A colon-separated list of directory pathnames that bash looks for commands in (page 285) |
PROMPT_COMMAND |
A command that bash executes just before it displays the primary prompt |
PS1 |
Prompt String 1; the primary prompt (default: '\s–\v\$ '; page 286) |
PS2 |
Prompt String 2; the secondary prompt (default: '> '; page 288) |
PS3 |
The prompt issued by select (page 551) |
PS4 |
The bash debugging symbol (page 536) |
REPLY |
Holds the line that read accepts (page 572); also used by select (page 551) |
Special Characters
Table 8-6 lists most of the characters that are special to the bash and tcsh shells.
Table 8-6. Shell special characters
Character |
Use |
NEWLINE |
Initiates execution of a command (page 269) |
; |
Separates commands (page 269) |
( ) |
Groups commands (page 271) for execution by a subshell or identifies a function (page 314) |
& |
Executes a command in the background (pages 131 and 270) |
| |
Sends output through a pipe (page 270) |
> |
Redirects standard output (page 122) |
>> |
Appends standard output (page 127) |
< |
Redirects standard input (page 124) |
<< |
Here document (page 553) |
* |
Any string of zero or more characters in an ambiguous file reference (page 134) |
? |
Any single character in an ambiguous file reference (page 134) |
\ |
Quotes the following character (page 40) |
' |
Quotes a string, preventing all substitution (page 40) |
" |
Quotes a string, allowing only variable and command substitution (pages 40 and 280) |
'...' |
Performs command substitution (page 327) |
[ ] |
Character class in an ambiguous file reference (page 136) |
$ |
References a variable (page 278) |
. (dot builtin) |
Executes a command (only at the beginning of a line, page 261) |
# |
Begins a comment (page 268) |
{ } |
Used to surround the contents of a function (page 314) |
: (null builtin) |
Returns true (page 579) |
&& (Boolean AND) |
Executes command on right only if command on left succeeds (returns a zero exit status, page 590) |
|| (Boolean OR) |
Executes command on right only if command on left fails (returns a nonzero exit status; page 590) |
! (Boolean NOT) |
Reverses exit status of a command |
$() (not in tcsh) |
Performs command substitution (preferred form; page 327) |
[] |
Evaluates an arithmetic expression (page 325) |