Chapter 13: User Environments
This book’s primary focus is on the parts of the Linux system that normally underlie server processes and interactive user sessions. But eventually, the system and the user have to meet somewhere. Startup files play an important role at this point, because they set defaults for the shell and other interactive programs. They determine how the system behaves when a user logs in.
Most users don’t pay close attention to their startup files, touching them only when they want to add something for convenience, such as an alias. Over time, the files become cluttered with unnecessary environment variables and tests that can lead to annoying (or quite serious) problems.
If you’ve had your Linux machine for a while, you might have noticed that your home directory accumulates a bafflingly large array of startup files over time. These are sometimes called dot files because they nearly always start with a dot (.), excluding them from the default display of ls and most file managers. Many of these are automatically created when you first run a program, and you’ll never need to change them. This chapter primarily covers shell startup files, which are the ones you’re most likely to modify or rewrite from scratch. Let’s first look at how much care you need to take when working on these files.
13.1 Guidelines for Creating Startup Files
When designing startup files, keep the user in mind. If you’re the only user on a machine, you don’t have much to worry about, because any errors affect only you and they’re easy enough to fix. However, if you’re creating startup files meant to be the defaults for all new users on a machine or network, or if you think that someone might copy your files for use on a different machine, this process becomes considerably more critical. If you make an error in a startup file and distribute it to 10 users, you might end up fixing this error 10 times.
Keep two essential goals in mind when creating startup files for other users:
Simplicity Keep the number of startup files small, and keep the files as short and simple as possible so that they’re easy to modify but hard to break. Each item in a startup file is just one more thing that can break.
Readability Use extensive comments in files so that the users get a good picture of what each part of a file does.
13.2 When to Alter Startup Files
Before making a change to a startup file, ask yourself whether you really should be making it. Here are some good reasons for changing startup files:
- You want to change the default prompt.
- You need to accommodate some critical locally installed software. (Consider using wrapper scripts first, though.)
- Your existing startup files are broken.
If everything in your Linux distribution works, be careful. Sometimes the default startup files interact with other files in /etc.
That said, you probably wouldn’t be reading this chapter if you weren’t interested in changing the defaults, so let’s examine what’s important.
13.3 Shell Startup File Elements
What goes into a shell startup file? Some things might seem obvious, such as the command path and a prompt setting. But what exactly should be in the path, and what does a reasonable prompt look like? And how much is too much to put in a startup file?
This section discusses the essentials of a shell startup file—from the command path, prompt, and aliases through the permissions mask.
13.3.1 The Command Path
The most important part of any shell startup file is the command path. The path should cover the directories that contain every application of interest to a regular user. At the very least, the path should contain these components, in order:
/usr/local/bin
/usr/bin
/bin
This order ensures that you can override standard default programs with site-specific variants located in /usr/local.
Most Linux distributions install executables for nearly all packaged user software in /usr/bin. Some are occasional differences that have crept in over the years, such as putting games in /usr/games and graphical applications in a separate location, so check your system’s defaults first. And make sure that every general-use program on the system is available through one of the directories just listed. If not, your system is probably getting out of control. Don’t change the default path in your user environment to accommodate every new software installation directory. A cheap way to accommodate separate installation directories is to use symbolic links in /usr/local/bin.
Many users create a bin directory of their own to store shell scripts and programs, so you may want to add this to the front of the path:
$HOME/bin
A newer convention is to place binaries in $HOME/.local/bin.
If you’re interested in systems utilities (such as sysctl, fdisk, and lsmod), add the sbin directories to your path:
/usr/local/sbin
/usr/sbin
/sbin
There is one small but controversial command path component to discuss: the dot. Placing a dot (.) in your path allows you to run programs in the current directory without using ./ in front of the program name. This may seem convenient when writing scripts or compiling programs, but it’s a bad idea for two reasons:
- It can be a security problem. You should never put a dot at the front of the path. As just one example of what might happen, an attacker could put a Trojan horse named
lsin an archive distributed on the internet. Even if the dot is placed at the end of the path, you’d still be vulnerable to an attacker anticipating typos such asslorks. - It is inconsistent and can be confusing. A dot in a path can mean that a command’s behavior will change according to the current directory.
13.3.2 The Manual Page Path
The traditional manual page path was determined by the MANPATH environment variable, but you shouldn’t set it because doing so overrides the system defaults in /etc/manpath.config.
13.3.3 The Prompt
Experienced users tend to avoid long, complicated, useless prompts. In comparison, many administrators and distributions drag everything into a default prompt. Even many shell default prompts are cluttered or otherwise mostly useless. For example, the default bash prompt contains the shell name and version number. Your choice should reflect your users’ needs; place the current working directory, hostname, and username in the prompt if it really helps.
Above all, avoid characters that mean something significant to the shell, such as these:
{ } = & < >
Take extra care to avoid the > character, which can cause erratic, empty files to appear in your current directory if you accidentally copy and paste a section of your shell window (> redirects output to a file).
This simple prompt setting for bash ends with the customary $ (the traditional csh prompt ends with %):
PS1='\u\$ '
The \u is an expression that the shell evaluates to the current username (see the PROMPTING section of the bash(1) manual page). Other popular expressions include:
\h The hostname (the short form, without domain names).
\! The history number.
\w The current directory. Because this can become long, you can limit the display to just the final component by using \W instead.
\$ $ if running as a user account; # if root.
13.3.4 Aliases
Among the stickier points of contemporary user environments is the role of aliases, a shell feature that substitutes one string for another before executing a command. Aliases can be efficient shortcuts that save some typing. However, they have several drawbacks:
- It can be tricky to manipulate arguments.
- They are confusing; a shell’s built-in
whichcommand can tell you if something is an alias, but it won’t tell you where it was defined. - They are frowned upon in subshells and noninteractive shells; they aren’t passed onto child shells.
One classic mistake when defining an alias is to add extra arguments to an existing command—for example, aliasing ls to ls -F. At best, this can make it difficult to remove the -F argument when you don’t want it. At worst, it can have severe consequences for the user who does not understand that they’re not using the default arguments.
Given these disadvantages, you should probably avoid aliases whenever possible; it’s easier to write a shell function or an entirely new shell script. A computer can start and execute shells so quickly that the difference between an alias and an entirely new command should be unnoticeable.
That said, aliases do come in handy when you wish to alter a part of the shell’s environment. You can’t change an environment variable with a shell script, because scripts run as subshells. (But you can instead define shell functions to perform this task.)
13.3.5 The Permissions Mask
As described in Chapter 2, a shell’s built-in umask (permissions mask) facility sets your default permissions. Include the umask command in one of your startup files to make certain that any program you run creates files with your desired permissions. There are two reasonable choices:
077 This mask is the most restrictive permissions mask; it doesn’t give any other users access to new files and directories. This is often appropriate on a multi-user system where you don’t want other users to look at any of your files. However, when set as the default, this mask p. 340 can sometimes lead to problems when your users want to share files but don’t understand how to set permissions correctly. (Inexperienced users have a tendency to set files to a world-writable mode.)
022 This mask gives other users read access to new files and directories. This can be a good choice on a single-user system because many daemons that run as pseudo-users won’t be able to see files and directories created with the more restrictive 077 umask.
Certain applications (especially mail programs) override the umask, changing it to 077 because they feel that their files are the business of no one but the file owner.
13.4 Startup File Order and Examples
Now that you know what to put into shell startup files, it’s time to see some specific examples. Surprisingly, one of the most difficult and confusing parts of creating startup files is determining which of several possible startup files to use. This section covers the two most popular Unix shells: bash and tcsh.
13.4.1 The bash Shell
In bash, you can choose from the startup filenames .bash_profile, .profile, .bash_login, and .bashrc. Which one is appropriate for your command path, manual page path, prompt, aliases, and permissions mask? The answer is that you should have a .bashrc file accompanied by a .bash_profile symbolic link pointing to .bashrc because there are a few different kinds of bash shell instance types.
The two main shell instance types are interactive and noninteractive, but we’re interested only in interactive shells, because noninteractive shells (such as those that run shell scripts) usually don’t read any startup files. Interactive shells are those you use to run commands from a terminal, such as the ones you’ve seen in this book, and they can be classified as login or non-login.
Login Shells
Traditionally, a login shell is what you get when you first log in to a system with the terminal using a program such as /bin/login. Logging in remotely with SSH also gives you a login shell. The basic idea is that the login shell is an initial shell. You can tell if a shell is a login shell by running echo $0; if the first character is a -, the shell’s a login shell.
When bash runs as a login shell, it runs /etc/profile. Then it looks for a user’s .bash_profile, .bash_login, and .profile files, running only the first one that it sees.
As strange as it sounds, it’s possible to run a noninteractive shell as a login shell to force it to run startup files. To do so, start the shell with the -l or --login option.
Non-Login Shells
A non-login shell is an additional shell that you run after you log in. It’s simply any interactive shell that’s not a login shell. Windowing system terminal programs (xterm, GNOME Terminal, and so on) start non-login shells unless you specifically ask for a login shell.
Upon starting up as a non-login shell, bash runs /etc/bash.bashrc and then runs the user’s .bashrc.
The Consequences of Two Kinds of Shells
The reasoning behind the two different startup files is that in the old days, users logged in through a traditional terminal with a login shell, and then started non-login subshells with windowing systems or the screen program. For the non-login subshells, it was deemed a waste to repeatedly set the user environment and run a bunch of programs that had already been run. With login shells, you could run fancy startup commands in a file such as .bash_profile, leaving only aliases and other “lightweight” things to your .bashrc.
Nowadays, most desktop users log in through a graphical display manager (you’ll learn more about these in the next chapter). Most of these start with one noninteractive login shell in order to preserve the login versus non-login model. When they do not, you need to set up your entire environment (path, manual path, and so on) in your .bashrc, or you’ll never see any of your environment in your terminal window shells. However, you also need a .bash_profile if you ever want to log in on the console or remotely, because those login shells don’t ever bother with .bashrc.
Example .bashrc
In order to satisfy both non-login and login shells, how would you create a .bashrc that can also be used as your .bash_profile? Here’s one very elementary (yet perfectly sufficient) example:
# Command path.
PATH=/usr/local/bin:/usr/bin:/bin:/usr/games
PATH=$HOME/bin:$PATH
# PS1 is the regular prompt.
# Substitutions include:
# \u username \h hostname \w current directory
# \! history number \s shell name \$ $ if regular user
PS1='\u\$ '
# EDITOR and VISUAL determine the editor that programs such as less
# and mail clients invoke when asked to edit a file.
EDITOR=vi
VISUAL=vi
# PAGER is the default text file viewer for programs such as man.
PAGER=less
# These are some handy options for less.
# A different style is LESS=FRX
# (F=quit at end, R=show raw characters, X=don't use alt screen)
LESS=meiX
# You must export environment variables.
export PATH EDITOR VISUAL PAGER LESS
# By default, give other users read-only access to most new files.
umask 022
In this startup file, the path has $HOME/bin at the front so that executables there take precedence over the system versions. If you need the system executables, add /sbin and /usr/sbin.
As described earlier, you can share this .bashrc file with .bash_profile via a symbolic link, or you can make the relationship even clearer by creating .bash_profile as this one-liner:
. $HOME/.bashrc
Checking for Login and Interactive Shells
With a .bashrc matching your .bash_profile, you don’t normally run extra commands for login shells. However, if you want to define different actions for login and non-login shells, you can add the following test to your .bashrc, which checks the shell’s $- variable for an i character:
case $- in
*i*) # interactive commands go here
command
--snip--
;;
*) # non-interactive commands go here
command
--snip--
;;
esac
13.4.2 The tcsh Shell
The standard csh on virtually all Linux systems is tcsh, an enhanced C shell that popularized features such as command-line editing and multi-mode filename and command completion. Even if you don’t use tcsh as the default new user shell (bash should be the default), you should still provide tcsh startup files in case your users happen to come across tcsh.
You don’t have to worry about the difference between login shells and non-login shells in tcsh. Upon startup, tcsh looks for a .tcshrc file. Failing this, it looks for the csh shell’s .cshrc startup file. The reason for this order is that you can use the .tcshrc file for tcsh extensions that don’t work in csh. You should probably stick to using the traditional .cshrc instead of .tcshrc; it’s highly unlikely that anyone will ever use your startup files with csh. And if a user actually does come across csh on some other system, your .cshrc will work.
Example .cshrc
Here is a sample .cshrc file:
# Command path.
setenv PATH $HOME/bin:/usr/local/bin:/usr/bin:/bin
# EDITOR and VISUAL determine the editor that programs such as less
# and mail clients invoke when asked to edit a file.
setenv EDITOR vi
setenv VISUAL vi
# PAGER is the default text file viewer for programs such as man.
setenv PAGER less
# These are some handy options for less.
setenv LESS meiX
# By default, give other users read-only access to most new files.
umask 022
# Customize the prompt.
# Substitutions include:
# %n username %m hostname %/ current directory
# %h history number %l current terminal %% %
set prompt="%m%% "
13.5 Default User Settings
The best way to write startup files and choose defaults for new users is to experiment with a new test user on the system. Create the test user with an empty home directory and refrain from copying your own startup files to the test user’s directory. Write the new startup files from scratch.
When you think you have a working setup, log in as the new test user in all possible ways (on the console, remotely, and so on). Make sure that you test as many things as possible, including the windowing system operation and manual pages. When you’re happy with the test user, create a second test user, copying the startup files from the first test user. If everything still works, you now have a new set of startup files that you can distribute to new users.
This section outlines reasonable defaults for new users.
13.5.1 Shell Defaults
The default shell for any new user on a Linux system should be bash because:
- Users interact with the same shell that they use to write shell scripts. (For many reasons, which I won’t expand upon here,
cshis a notoriously bad scripting tool—don’t even think about it.) bashis the default on Linux distributions. p. 344bashuses the GNU readline library to accept input, and therefore its interface is identical to that of many other tools.bashgives you fine, easy-to-understand control over I/O redirection and file handles.
However, many seasoned Unix wizards use a shell such as csh or tcsh simply because it’s what they’re most familiar with, and they can’t bear to switch. Of course, you can choose any shell you like, but choose bash if you don’t have any preference, and use bash as the default shell for any new user on the system. (Users can change their shell with the chsh command to suit their individual preference.)
There are plenty of other shells out there (rc, ksh, zsh, es, and so on). Some are not appropriate as beginner shells, but zsh and fish are sometimes popular with new users looking for an alternative shell.
13.5.2 Editor
On traditional systems, the default editor is vi or emacs. These are the only editors virtually guaranteed to exist (or at least be available) on nearly any Unix system, which means they’ll cause the least trouble in the long run for a new user. However, Linux distributions often configure nano to be the default editor, because it’s easier for beginners to use.
As with shell startup files, avoid large default editor startup files. A little set showmatch in the .exrc startup file (to have vi show matching parentheses) never hurt anyone, but steer clear of anything that significantly changes the editor’s behavior or appearance, such as the showmode feature, auto-indentation, and wrap margins.
13.5.3 Pager
The pager is a program, such as less, that shows text one page at a time. It’s perfectly reasonable to set the default PAGER environment variable to less.
13.6 Startup File Pitfalls
Avoid these pitfalls in startup files:
- Don’t put any kind of graphical command in a shell startup file. Not all shells run in graphical environments.
- Don’t set the
DISPLAYenvironment variable in a shell startup file. We haven’t looked at graphical environments yet, but this can cause your graphical session to misbehave. - Don’t set the terminal type in a shell startup file.
- Don’t skimp on descriptive comments in default startup files.
- Don’t run commands in a startup file that print to the standard output.
- Never set
LD_LIBRARY_PATHin a shell startup file (see Section 15.1.3).
13.7 Further Startup Topics
Because this book deals only with the underlying Linux system, we won’t cover windowing environment startup files. This is a large issue indeed, because the display manager that logs you in to a modern Linux system has its own set of startup files, such as .xsession, .xinitrc, and the endless combinations of GNOME- and KDE-related items.
The windowing choices may seem bewildering, and there is no one common way to start a windowing environment in Linux. The next chapter describes some of the many possibilities. However, when you determine what your system does, you might get a little carried away with the files that relate to your graphical environment. That’s fine, but don’t carry it over to new users. The same tenet of keeping things simple in shell startup files works wonders for GUI startup files, too. In fact, you probably don’t need to change your GUI startup files at all.