edit · print · PDF

Please note that all the SIEpedia's articles address specific issues or questions raised by IAC users, so they do not attempt to be rigorous or exhaustive, and may or may not be useful or applicable in different or more general contexts.

Environment Variables

Many Unix/Linux programs, including the shell, need information about your account and what you are doing in order to work correctly and efficiently.

For instance, the shell may need to know what kind of terminal you are using, or what your home directory is, or in what directory it must look for commands and executables.

So, UNIX uses environment variables (EV for short) to store information that you'd rather not worry about, either because it's something specific to your platform or shell, or because it is set by your system manager.

Environment variables are managed by the shell, so different shells may have different sets of EVs, and they can be defined in a different way. (This often causes confusion among users of the tcsh shell when they try to set the EVs after logging in as root in a bash shell).

This presentation applies to a bash shell in a desktop Linux PC.

How to list, define and modify Environment Variables

A list of all defined EVs can be obtained by using the command printenv:
printenv

To ask about a particular variable:
printenv HOME

Alternatively (note the dollar sign):
echo $HOME
To set or redefine a variable, the command is export NAME=value. For instance:
export EDITOR=/usr/local/bin/emacs
export PRINTER=lw3

To have the shell forget that an environment variable ever existed, use the unset command, for instance:
unset TINYTIM

By convention, the names of environmental variables are all uppercase, though there's nothing to enforce this convention.

Digression: The () Subshell Operator

A quick digression on the subshell operator. If we wish to run some commands in a subshell, so as not to affect the primary shell, the () subshell operator is a very useful tool.

For instance, if we wish to know what time it is in Vladivostock, we can accomplish it by changing accordingly the TimeZone TZ variable, and then executing the date command:
(export TZ="Asia/Vladivostok" ; date)
By doing it in a subshell, we do not need to remember what value TZ had before, and change it back after running the date command.

This trick is also useful, for example, to run a command in a different directory without changing the current work directory:
(cd $HOME ; latex mypaper ; dvips mypaper -o)

You can use the subshell operator to test definitions or modifications of variables before committing to them.

Parent-Child Relationships

Each subprocess, or a new subshell, will get its own copy of its parent's environment variables. For instance, if you write and execute a shell script, it will "see", or "inherit", all the environment variables present in the parent shell.

However, the opposite is not true: a UNIX process can not change its parent's environment; any changes it keeps to itself. So, if you write a shell script where you define new EVs, and start it, when it's finished there is no trace of them.

An easy way to check it is to use a subshell:
( printenv )
(export OFFICE=2104 ; printenv OFFICE ) ; printenv OFFICE
variable OFFICE is undefined in the parent shell.

Some of the Most Important Environment Variables

Here we'll have a look at those Environment Variables which are specially important, and sometimes need some modifications or changes by the user (for instance after installing a new software package).

PATH

It contains the command search path, that is a list of directories in which the shell looks to find commands. In particular, whenever a command is typed, the shell will go through the list of directories, in the order they appear, until it finds it.
printenv PATH

This is not completely true, though. Actually the shell doesn't search directly the path, but uses a hash table to find the command quickly. That's why, if a new executable is added in any of the directories in PATH, from a tcsh shell you need to type rehash to have the shell rebuild the hash table (this is not necessary in bash).

As the order in $PATH is important, one should pay attention to whether prepending or appending a new directory to this variable.

An empty entry in PATH (: as first or last character, or :: in the middle) means "the current directory". So, if you can't launch an executable in the current directory by just typing its name (you get an error message like "Command not found"), add the current directory to the PATH.
export PATH=${PATH}: (Actually this might not be a very good idea; see http://unix.stackexchange.com/questions/65700/is-it-safe-to-add-to-my-path-how-come)

Generally it is not a problem to have directories appear twice or more times in the PATH, unless the variable becomes too long.

To add a directory to PATH, use the following syntax:
export PATH="${PATH}:/path/for/new/directory"
-or-
export PATH="/path/for/new/directory:${PATH}"

Be very careful with the syntax, otherwise you can delete or set the variable in a incorrect or inconsistent way. For example:
(export PATH=" "; date ; who)
PATH is set to null by mistake, and nothing works anymore.

LD_LIBRARY_PATH

This is a list of directories where the shell looks for shared object libraries. If you install a new package, with some new shared objects, you need to either copy or link those files into a directory already included in LD_LIBRARY_PATH, or add the new directory to LD_LIBRARY_PATH.
printenv LD_LIBRARY_PATH

If, when you launch some executable, you obtains error messages like "error while loading shared libraries: libtermcap.so.2", it means that either the missing library is not installed, or it's installed in a directory not included in LD_LIBRARY_PATH.

Note that Linux has already a default set of directories where to look for libraries.

To add a directory to LD_LIBRARY_PATH, follow the same prescriptions given for PATH, for instance:
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:/home/ncaon/lib_Linux"

LC_ALL, LANG

These variables define the language used by your shell. Their default value used to be "es_ES", that is spanish (not sure if it still is).

Please take into account that some programs or scripts may fail or behave strangely if run in a spanish-speaking shell. Moreover, somebody may find the error messages printed in spanish somewhat funny (for instance, ViolaciĆ³n de segmentos: is this some sort of sex crime that a polygon might commit?) and prefers to interact with an english-speaking shell.

If this is your case, you can redefine the above variables to, e.g., American English:
export LC_ALL=en_US
export LANG=en_US

Ohter languages are also available, and you can play around with them. For instance see how "command not found" is translated in other languages:
(export LC_ALL=en_US ; export LANG=en_US ; aaaaa)
(export LC_ALL=es_ES ; export LANG=es_ES ; aaaaa)
(export LC_ALL=fi_FI ; export LANG=fi_FI ; aaaaa)
(export LC_ALL=de_DE ; export LANG=de_DE ; aaaaa)
(export LC_ALL=fr_FR ; export LANG=fr_FR ; aaaaa)

By who and where are EVs defined?

Broadly speaking, we can divide the Environment Variables into three groups:

  • Variables set (and modified automatically) by the Operating System and by the shell. This is usually done by startup files such as /etc/bashrc and /etc/profile, which in turn "source" other files. Such variables are, for instance:
    • HOME;
    • USER;
    • HOST;
    • PWD, contains the absolute pathname of the current directory, and it's reset automatically by the cd command;
    • SHELL, the absolute pathname of your login shell;
    • TERM, which identifies the type of terminal you are using;
    • PATH and (perhaps) LD_LIBRARY_PATH, but only including the basic directories;
  • Variables set or modified by the System Administrators (see later on) to best configure your environment and allow locally installed programs to work:
    • PATH and LD_LIBRARY_PATH are expanded by adding the relevant directories for programs such as Latex, Intel Fortran compiler, Java, etc.;
    • LANG, LANGVAR and LC_ALL set the language you are working in (I think Spanish is the default);
    • PGPLOT_DIR and PGPLOT_FONT, required by pgplot
    • IRAFARCH, which tells IRAF the architecture of the machine (for instance linux64, or macintel)
  • Variables set or modified (with care) by the users themselves, in their startup files (see below).

Shell options

The behavior of the bash shell is controlled by a number of (boolean) options, some specific to the bash shell, others applying to sh-type shells in general. To see a list of such variables, together with their "on" or "off" value, execute the commands:
shopt | column -t     # bash-specific options
set -o | column -t     # more general options
(the | column -t pipe makes the output prettier)

By convention, shell variables are all lowercase (while environment variables are normally uppercase).

To change the value of a shell option, type:
shopt -s <OPTNAME>     # enable (set) option
shopt -u <OPTNAME>     # edisable (unset) option
shopt <OPTNAME>     # print current value

You may play around with some of those variables, for instance by enabling "autocd" you can cd to a directory by just writing its name.

The complete list of shell options and their meaning can be found in http://wiki.bash-hackers.org/internals/shell_options

As for the more general options, you can modify them using commands:
set -o <OPTNAME>     # set (enable) option
set +o <OPTNAME>     # unset (disable) option
(Using "-o" to enable an option and "+o" to disable it may look counter-intuitive, but it agrees with the standard way of adding flags on the linux command line prefixed by the "-" sign).

A list of such options can be found in http://wiki.bash-hackers.org/commands/builtin/set

User's Startup files

Sad but true stories ...

Excerpts from some CAU tickets:

Q.: All of a sudden I can't access my home
A.: Files .cshrc and .login are missing. Fix: standards .cshrc and .login files are copied into the user's home.

Q.: I can't login into my account anymore! I only did a little change to the PATH variable in my .cshrc file...
A.: PATH was changed using an incorrect syntax, making this variable useless.

Q.: Why are my print jobs sent to printer lwA, after I asked the CAU to configure my machine with printer lwB as the default one?
A.: The printer was somehow redefined in the user's .login file.

Q.: I can't run Latex in my machine!
A.: The user had commented out, in his .cshrc file, the line:
# source /usr/glob/user/.cshrc

What are the basic startup files?

Which startup files the bash shell reads depends on whether its is an interactive login shell, an interactive non-login shell, or a non-interactive shell.

The last case applies typically to shell scripts, which are run without interaction with the user. In such cases the bash shell will just inherit the environment of the parent shell.

We'll focus on the first two cases.

Login and non-login shells

interactive login shell: The shell reads first /etc/profile, then ~/.bash_profile. If the latter file is not found, it attempts to read ~/.bash_login and ~/.profile. When the login shell exits, it reads ~/.bash_logout, if it exists. (By "read", we mean that the file is read and all command there contained are executed).

interactive non-login shell: In non-login shells, bash reads ~/.bashrc.

While the distinction between login and non-login shells was clear in the old days, now it's somewhat blurred. For instance Linux has interactive shells started by the windows system, remote shells, and other shells which, depending on how they are invoked, might or might not be login shells. So it is not always clear when a new shell or application reads or not the .login file.

Empirically, I've found the following (by having .bashrc and .bash_profile print a message when they are read):

  • Konsole, XTerm, XGterm: all are non-login shells (so they do not read ~/.bash_profile), unless they are started with the flag "--login".
  • ssh: opens a login shell: ~/.bash_profile is read.
  • bash (new subshell): reads only ~/.bashrc. To have it read also ~/.bash_profile, start it with bash --login
  • () subshell: reads neither .bashrc nor .bash_profile (but of course inherits environment).

'How can this be handled?' Personally I put everything in my ~/.bash_profile, and make sure that all shells I start are login. For the "Konsole" terminal application, I edited the profile (Settings -> Edit Current Profile) and put "/bin/bash --login" in the Command box. Of course other solutions are possible.

Tip: How can I determine whether my current shell is login or not-login? Perhaps the simplest way is to check the value of the variable login_shell, typing: shopt login_shell. Value is "on" for login shells, "off" for non-login shells.

Standard startup files: do's and dont's

Every time a new (bash) account is created, it comes with default ~/.bashrc and ~/.bash_profile files, set up by the system administrators.

They should be modified with much care. For instance, never ever delete or comment out the line, in the ~/.bash_profile file:
source /usr/glob/user/SIE/.bashrc_SIE

This file, together with other auxiliary files, builds and defines your environment. If you remove this line, your environment will be incomplete and many things won't work!

If you are doing some file input or output in a setup file (such as reading or sourcing from a file, or writing some date in a log file), please use absolute pathnames, not relative ones. For example:
source .aliases     # Wrong!
echo "Logged in at `date`" >> login.log     # Wrong!
source ~/.aliases     # OK!
echo "Logged in at `date`" >> ~/login.log     # OK!
The first two lines won't work if the (sub)shell is started from somewhere else than the home directory.

Keep the .bashrc and .bash_profile clear and neat

A lot of stuff can go inside a .bash_profile files: new or redefined environment and shell variables, aliases, prompts, perhaps some conditional instructions depending on which machine or platform you are using, functions, etc.

So, instead of ending up with a huge .bash_profile files including thousands of lines, you can organize them in separate files. ~/.bash_profile might then look like this:


# Default instruction placed by the System Administrator
@@source /usr/glob/user/SIE/.bashrc_SIE@@



# Modify path to append my personal bin directory:
export PATH=${PATH}:~/bin

# Set prompt
PS1='\[\e[1;33m\]\u@\h> \[\e[m\]'
# from https://www.linux.com/learn/tutorials/772396-how-to-make-a-fancy-and-useful-bash-prompt-in-linux-

# Define/modify environment variables needed by some personal packages
source ~/.export_bash

# Load aliases:
source ~/.alias_bash

# Set bash shell options (default values are generally OK):
source ~/.options_bash

# Define functions:
source ~/.functions_bash

# Platform dependent stuff (if you happen to share startup files across different platforms, for instance Linux and Mac OS X):
if [[ $OSTYPE == "Linux" ]]; then
   source ~/.linux_specific
fi

# Host dependent stuff (for instance a burro, or a machine with some specific configuration):
if [[ $HOST == "esel" ]]; then
   source ~/.esel_specific
fi
 

(The "_bash" part of the file name differentiates such files from the equivalent ones with a "_cshrc" suffix which I still keep around for when I need to run a tcsh shell).

Bash functions

Aliases are basically shortcuts to often-used commands. For more complex things, for instance passing arguments or using conditional expressions, one can take advantage of bash functions. This is an advanced topic, discussed in several websites, for instance http://www.thegeekstuff.com/2010/04/unix-bash-function-examples/ or http://ryanstutorials.net/bash-scripting-tutorial/bash-functions.php

edit · print · PDF
Page last modified on July 25, 2016, at 04:13 PM