======================================== Shell Variables - Basics (non-scripting) ======================================== -Ian! D. Allen - idallen@idallen.ca - www.idallen.com One of the things the Unix shell does to make work easier is let you define variables that can contain text (or numbers) that you can insert into your command lines and in shell scripts. Also, some variables affect how the shell operates, e.g. the $PATH variable tells the shell where to look for command names to execute. You assign to a variable using '=' with no blanks around it: $ x=hello $ y="hello there this has blanks" $ z='this has blanks too' $ myvar=this\ also\ has\ blanks Variable names must start with a letter or underscore. Variables expand in command lines if you put Dollar Signs in front of their names, e.g. $ x=hello $ _Y=there $ echo "$x" hello $ echo "$x $_Y, $x" hello there, hello $ y="hello there this has blanks" $ echo "$y" hello there this has blanks The use below is wrong because the shell splits the line on blanks: $ x=hello there there: command not found $ echo "$x" hello Quoting to hide Dollar Signs ---------------------------- Unless you protect the Dollar Signs ('$') using single quotes or backslashes, the shell will find and expand variables by looking for Dollar Sign metacharacters, even inside double-quoted strings: $ x=hello $ echo "the variable x contains $x" the variable x contains hello $ echo "protect the dollar using backslash \$x" protect the dollar using backslash $x $ echo 'protect the dollar using single quotes $x' protect the dollar using single quotes $x Single quotes and backslashes will protect dollar signs ('$' and \$). Unrecognized variables are empty -------------------------------- By default, undefined variables are not an error and simply expand quietly to be nothing: $ echo "this is $nosuchvariable expanding" this is expanding I recommend that you set the "nounset" shell option that will cause the shell to signal an error for undefined variables: $ echo "this is $nosuchvariable expanding" this is expanding $ set -o nounset $ echo "this is $nosuchvariable expanding" bash: nosuchvariable: unbound variable With "nounset" set, typing mistakes in variable names will cause errors instead of silently doing the wrong thing. Consider this: # DIR=/home/me # my home directory # rm -rf "$DIRR/bin" # typing error causes "/bin" to be removed With "nounset" set, the above typing mistake would result in an error message about the undefined $DIRR variable, instead of letting the unknown variable expand to be nothing and thus deleting the whole system /bin directory. Consider this, worse, case: # INSTALL=opt/installdir # where to install the software # rm -rf "/$INSTAL" # small typing error wipes out everything Put "set -o nounset" in your .bashrc for all your shells! Local variables and Environment (global) variables -------------------------------------------------- Variable definitions can be local to the current shell process (the default), or they can be exported (using the "export" built-in command) to the "environment" of child processes: $ x=foo # define a local variable named x containing foo $ /bin/bash # start a child process (another shell) bash$ echo "see x $x" see x # variable x was not exported to child environment bash$ exit $ export x # export the variable $ /bin/bash # start a child process (another shell) bash$ echo "see x $x" see x foo # variable x was exported; value is inherited Exported variables are also called "environment" variables; since, they are part of the starting environment of a new child process. Local variables are not passed to child processes. By convention, Environment Variables (exported variables) have names that are all UPPER-CASE, e.g. PATH, HOME, USER, LOGNAME, TERM As with anything in a child process (also including such things as umask and current directory), setting a variable (local or environment) in a child process does not affect any parent processes. Parent processes cannot reverse-inherit shell variable values from child processes. Once the child has a copy of the variable, further changes to the variable don't affect the parent shell (but will affect subsequent children of the child shell). Once a child process has taken a copy into its environment of all exported variables, the parent process has no more effect. Changing a variable in a parent process will not change the value in an already-running child process. New child processes only get a copy of the exported variables at the time the new process is started. Pre-set Environment Variables ----------------------------- When you log in to a Unix system, your login shell has many variables already set for you. Some of these variables are local to your login shell; many are already exported to any child processes you start from the shell. Typing "set" with no arguments lists all the variables (both local and exported) and their current values. Typing "printenv" or "env" with no arguments lists only environment (exported) variables. $ set | wc 85 108 1697 $ printenv | wc 40 45 876 Some important Environment Variables ------------------------------------ PATH - shell command search path USER - the login userid of the current user (some systems use LOGNAME) HOME - the home directory of the current user (cd takes you here) TERM - the terminal type SHELL - your login shell (not necessarily your current shell) $ cd ; pwd /home/idallen $ HOME=/bin ; cd ; pwd /bin $ HOME=/usr/bin ; cd ; pwd /usr/bin $ HOME=/home/$USER ; cd ; pwd /home/idallen Double-Quote all uses of variables ---------------------------------- When the shell finds and expands a variable (looking for the leading Dollar Sigh), the expanded text may itself contain more shell meta-characters that the shell will act on, e.g. blanks or GLOB characters. This is almost *NEVER* what you want to have happen! You can prevent the shell from re-processing the text substituted by variables, by expanding the variables inside double quotes, where the quotes hide the meaning of any other possible metacharacters that might be hidden inside the variable contents: $ x='* hi *' $ echo "$x" # note the use of double quotes * hi * $ ./argv "$x" Argument 0 is [./argv] Argument 1 is [* hi *] $ echo $x # note the lack of double quotes (BAD! BAD! BAD!) file1 file2 hi file1 file2 $ ./argv $x Argument 0 is [./argv] Argument 1 is [file1] Argument 2 is [file2] Argument 3 is [hi] Argument 4 is [file1] Argument 5 is [file2] $ x='*' # x contains an asterisk (GLOB character) $ echo "$x" # remember to double-quote the variable * # GLOB character does not expand (good!) $ echo $x # unquoted variable is not safe! file1 file2 # GLOB character expands (BAD BAD BAD!) $ msg='* warning *' # set a prefix for a warning message $ echo "$msg test" # double-quoted variable is safe to use * warning * test # GLOB characters do not expand (good!) $ echo $msg test # unquoted variable is not safe! file1 file2 warning file1 file2 test # GLOB characters expand (BAD!) You must remember to put double quotes around any shell variables that might contain shell metacharacters, otherwise the metacharacters will be expanded by the shell and the result may not be what you want. Always double-quote your variables, to prevent the shell from splitting the interpolated text on blanks and from expanding any GLOB characters in the interpolated text. Always put double-quotes around variables when you expand them. Double-quote all uses of variables, to stop GLOB expansion! Variables you should know ------------------------- You should know the meaning of the following shell variables: $TERM $HOME $PATH $SHELL $USER and/or $LOGNAME $$ $TERM: Set during login to contain the Unix name of the type of your terminal. Common values are "vt100", "xterm", "ansi", and "linux". When in doubt about the type of terminal you are on, set TERM to "vt100". Don't use vi or vim without having a correct $TERM set. $HOME: Set during login to be the absolute path to your home directory. The shell alias "~" is a synonym for $HOME if used at the start of a pathname, e.g. ~/foo is the same as $HOME/foo (Note: ~userid at the start of a pathname is expanded by the shell to be the home directory of the userid "userid", e.g. ~idallen/foo might be expanded by the shell to be /home/idallen/foo .) $PATH: Set during login to be a colon-separated list of directories in which the shell will look when it tries to find an executable file that matches a command name. $PATH is not used if the command name you type already contains any slashes. The "which" command looks for a command name in your $PATH. The "whereis" command ignores PATH. $SHELL: Set during login to be the pathname of the Unix Shell you are assigned in the password file (/etc/passwd). (This may or may not be the shell you are currently running, since you are free to start other shells once you have logged in. $USER or $LOGNAME: Set during login to be your account name, e.g. abcd0001. Some systems set $LOGNAME instead of (or in addition to) $USER. $$: The process ID of the current shell. Since every running process has a unique ID, $$ is often used when creating unique temporary file names in the /tmp directory, e.g. tmp="/tmp/tempfile$$" date >"$tmp" ; wc "$tmp" ; sort "$tmp" -- | Ian! D. Allen - idallen@idallen.ca - Ottawa, Ontario, Canada | Home Page: http://idallen.com/ Contact Improv: http://contactimprov.ca/ | College professor (Free/Libre GNU+Linux) at: http://teaching.idallen.com/ | Defend digital freedom: http://eff.org/ and have fun: http://fools.ca/