Using The Shell

Revision / Modified: Feb. 28, 2002
Author: Tom Berger

Original documents:
http://www.mandrakeuser.org/docs/basics/bshell.html http://www.mandrakeuser.org/docs/basics/bshell2.html http://www.mandrakeuser.org/docs/basics/bshell3.html http://www.mandrakeuser.org/docs/basics/bshell4.html http://www.mandrakeuser.org/docs/basics/bshell5.html http://www.mandrakeuser.org/docs/basics/bshell6.html

The Ultimate Power Tool

Every once in while you get to hear the litany that 'the command line' is 'out of date', 'arcane' and so on. And that people and operating systems should get rid off it.

The truth is: you can use Linux without knowing anything about the shell. You can boot your system directly into X and shut it down from there. You can use the Mandrake Control Center and Webmin to configure all and every aspect of your system.

It is my conviction, however, that using Linux without the shell is like driving your car in first gear only. Sure, it's easier at a first glance and you get where you want most of the time. But it's slow and takes the fun out of driving.
And that's it: the command line is fun. It's like the biggest collection of building blocks you've ever seen. And these blocks can do the most amazing things and what's more, you can make them cooperate, thus achieving highly complex tasks within a few lines. This is because in Unix the shell isn't just a simple command interpreter like DOS in Windows, but a fully fledged programming environment.

That isn't to say it is easy learning your ways around the shell. Well, it's learning, so what do you expect? ;-) But believe me that it can be highly rewarding. And you will be regarded as a Unix wizard by most outsiders in a rather short period of time *grin*.

In order to get some ideas what a shell is anyway, a little background knowledge might be useful

Terminals, xterms And Shells

Back in the days when Unix was born, the now dominant breed of 'personal computers' was completely unknown. The machines then referred to as 'computers' were bulky brutes using tapes and magnetic memory (hence the term 'core' for system memory). With the PDP-11 by DEC (now Compaq) introduced in 1970, a 'small' (thus called 'mini') and rather cheap computer appeared on the market and turned almost instantly into a big hit with universities, most of which were for the first time able to buy a computer (the PDP-11 was a real bargain at 10000$).

The operating systems for these machines were written in assembler, machine code, thus being highly efficient but also being totally and utterly unportable. Each computer vendor sold his operating system along with his machines.
The awkwardness of this situation was soon realized and attempts were started to create an operating system which could be run on more than one brand of machines. 1969 Ken Thompson wrote the first code of what was to become known as Unix (a pun on MULTICS, an earlier project Thompson had been working on). However, things really got started when Dennis Ritchie came up with a new programming language for this new operating system, called 'C'.
Although Unix was less efficient than the vendor supplied operating systems, it had three decisive advantages: it could be ported to any machine you happened to have around, the included 'C' language made programming by several magnitudes easier and it was free. Soon, universities all over America began equipping their machines with Unix.

Terminals

So Unix was (and is) an operating system running on a wide variety of machines. What about the people who used these machines? These people connected to these machines via 'dumb' terminals, i.e. machines consisting of a keyboard, a monitor and enough electronics to hold a connection to the central computer. What users at these terminals did was basically teletyping, thus the string 'tty' for terminal device files or the name of the 'getty' command.

You may ask yourself what all this has got to do with the topic at hand. Well, the vendors of these terminals couldn't agree on a standard. That is, each brand of terminals had its own keyboard layout, its own method of displaying characters on the screen, its own ideas what signals sent and received represented what characters or control codes and so on.
In order to cope with this chaos, a central file containing all the different and differing terminal capabilities was created, the termcap. Open '/etc/termcap' in a text viewer and stand amazed (or frightened ;-)).

Linux terminals mostly use either 'vt100' or 'linux' as their terminal type.

xterms

In the early Eighties, the X Window System, a graphical subsystem for Unix, was developed. XFree86 was forked from this system in the early 90s in order to write a version more stable on the then fledging Intel-based Unix clones like FreeBSD, NetBSD or Linux.

The first and foremost use of X was running multiple 'virtual' terminals on it. X even came with such an application, 'xterm'. Therefore you'll find that 'xterm' and 'virtual terminal' are often used synonymously. When you read 'open an xterm', you don't actually have to install and use the 'xterm' program, any other terminal emulator, like rxvt, konsole, aterm, eterm, wterm, etc, will do, too.

A terminal emulator (another name for 'virtual terminal') connects to the system via a pseudo tty device file, pty, and uses its own display standard, 'xterm'. This might lead to a slightly different behavior of some keys or applications on different terminal emulators, depending on how good these emulators emulate the 'xterm' display standard.

Shells

In order to run programs on a terminal, a shell is needed. The shell is that part of an operating system which communicates with the user and allows communication between commands.

The first 'real' Unix shell, 'sh', was written around 1975 by Steve Bourne, and is thus also called the 'Bourne shell'. Soon others developed, some based on the original Bourne shell like (pd)'ksh' or 'zsh', which are often used as the standard shell on proprietary Unixes, or the shells which implemented features from the 'C' programming language, like 'csh' or 'tcsh'.

In Linux, the standard shell is 'bash', the GNU Bourne-Again Shell (well, Unix humor ...). It is very powerful (some would say, bloated), the compressed (!) man page alone weighs some 50 KB.

First Steps On The Shell

First of all, a little note: you should not use the shell as 'root' for everyday tasks, especially if you are just beginning to use it. You can't damage the system accidentally (or even intentionally) when being a user; as 'root', however, a simple typo can have unforeseen and severe consequences.

The first thing you see after logging in or opening an xterm window is the prompt. The standard prompt on Mandrake Linux consists of your user name, the name of the machine you are logged into (if none is set, 'localhost' is used), the directory where you are (the 'working directory') and the prompt sign:

[tom@belbo tom]$

I'm logged in as user 'tom' on my machine which I've called 'belbo' and I'm in my home directory, '/home/tom'. The prompt for 'root' looks like this:

[root@belbo root]#

You see that apart from the different name, the prompt sign has changed from a '$' to a '#'. It is traditional on Bourne shells to end a user's prompt with '$' and root's prompt with '#'.

Every aspect of the prompt is customizable, you will read about that later on.

To run a command, you type the name of the command after the prompt and then hit the <ENTER> key. The shell searches for the command in its search path (more on that later, too), runs the command, prints the output of the command (if any) on the terminal and presents you with a new prompt when the command is finished:

[tom@belbo tom]$ whoami
tom
[tom@belbo tom]$

By the way, it does not matter where the cursor is when you hit the ENTER key, the shell will always read the complete line.

Basic commands are 'ls' (list directory), 'cp' (copy), 'mv' (move / rename), 'cd' (change directory), each coming with a wealth of options listed in their respective man page (man ls, man mv etc).

Before you start off into shell land, a quick introduction into terminology. Commands take options and/or arguments:

mv -i file dir

'-i' is an option to the 'mv' command, whereas 'file' and 'dir' are arguments. Available options are explained in detail in the command's man page (man mv in this example), whereas arguments are supplied by you. Options modify how a command works, whereas arguments determine on which data the command should be performed.

So far, this looks very much like the DOS shell almost everyone hates and despises (and rightfully so). Time to introduce some mechanisms which make your shell life a much more pleasurable experience.

Unix (and its younger brother Linux) have been born on the command line. Because of this, the command line in Unix features a lot of mechanism to spare you some menial typing. This page introduces some of them.

Auto-Completion

What is the shortest way to switch your current working directory from your home directory to the directory '/usr/src/linux/Documentation/isdn/' using the 'cd' ('change directory') command? It's

cd /u<TAB>sr<TAB>l<TAB>/D<TAB>is<TAB>

This is called 'automatic command line completion' and it is indispensable. Let's have a closer look at the example:

cd /u<TAB>

expands to cd /usr/. Easy. Next

cd /u<TAB>sr<TAB>

expands to cd /usr/src/. If you just enter cd /u<TAB>s<TAB>, you will be presented with a choice of three subdirectories of '/usr' which all match this pattern ('cd /u*/s*'): '/usr/sbin', '/usr/share' and '/usr/src'.

So the <TAB> key is a handy means to search directories for files or subdirectories you know the first letters of. For example ls /usr/bin/zip<TAB> gives you a list of all files and subdirectories in '/usr/bin' that start with the letters 'zip'. Of course there are much more powerful commands for this tasks, but it does the trick when you're in a hurry.
Furthermore it comes in handy with really long filenames. Say you want to install an RPM called 'boomshakalakwhizbang-4.6.4.5-mdk586.rpm'. You type rpm -i boom<TAB> and if there are no other files in this directory that match this pattern, the shell will fill in the rest by itself.

cd /u<TAB>sr<TAB>l<TAB>

expands to cd /usr/src/linux and waits for a decision. There are two directories in '/usr/src' that match: '/usr/src/linux-[...]' and '/usr/src/linux'. How do you tell the shell you want the latter? Append a slash ('/'), thus indicating the end of this last name.
Presume you are not sure if it was '/usr/src/linux/Documentation' or '/usr/src/linux/documentation'. As you may know, Linux discriminates between upper and lower case. If you have read carefully until here, you know the answer already:

cd /u<TAB>sr<TAB>l<TAB>/d<TAB>

expands to '/usr/src/linux/drivers/'. Looks like it was 'Documentation' (with a capital 'D') then.

This kind of completion works for commands, too:

[tom@belbo tom]$ gre<TAB>
grecord grefer grep
[tom@belbo tom]$ gre

Here the shell presents me with a list of all the commands it knows about which start with the string 'gre'.

Commandline History

Using the up-arrow key you can scroll through all the shell commands you have issued on that console recently. Using the down-arrow key you can scroll back again. Together with the SHIFT key, you can scroll through previous output on the console. You can also edit 'old' command lines and issue them again.

Pressing <CTRL r> puts the shell into "reverse-i(ncremental)-search" mode. Now type the first letter of the command you are looking for:

(reverse-i-search)`':. Typing 'i' may change this line to:

(reverse-i-search)`i': isdnctrl hangup ippp0

If you now press the <ENTER> key, this command will be executed again. If you press the left or right cursor key or <ESC> instead, you will have this command on a normal command line where you can edit it.

Commandline Editing

You can navigate and edit the command line with the cursor and the function keys ('Home', 'End' etc), if you like, but there are also keyboard shortcuts for most standard editing tasks:

As you venture deeper into Linux land, you'll find that these keyboard shortcuts are also used in other applications when it comes to entering text, for example in browser input fields.

Available Shell Shortcuts

Mandrake Linux comes with a row of shortcuts, some are native features of bash, some are pre-configured (you'll learn later on how to configure your own shortcuts).

Since the home directory is the focus point of activity for every user, many Unix systems provide special shortcuts for it.
'~' for example is a short form for the name of your home directory. Let's say you are in some far away directory and want to copy a file called 'sometext' to the directory 'docs' in your home directory. Instead of typing cp sometext /home/myusername/docs, you type cp sometext ~/docs which is much shorter, but has exactly the same effect.
In theory, this also applies to the 'cd' command. cd ~ would take you to your home directory, no matter where you are. But even that was considered too much typing. Just type cd and you're back home.

Mandrake Linux provides you with a set of pre-configured shortcuts (called 'aliases'):

Now that you are a bit more familiar with the shell and some of the shortcuts it provides, it's time to have a look what you can actually do with it besides running simple commands.

Command Queuing

In time you will become more aware of frequently used combinations of commands. You might want to issue all commands in one line and then direct your attention elsewhere. No problem. The shell lets you put various special queuing characters between commands. This section introduces the two most important of them.
Notice that I will use white spaces around these characters for clarities' sake. You don't need them in real life, there's no difference between 'ls -a ; du -hs' and 'ls -a;du -hs'.

command1 ; command2

executes command1 first and then command2, even if command1 returned an error.
Example:
ls -a ; du -hs
will first print a complete directory listing to the screen and then the total amount of space the files in this directory and its subdirectories take up.

command1 && command2

runs command2 if and only if command1 completed without an error.
Example:
$ ls -a bogusdir && du -hs
will return ls: bogusdir: No such file or directory and 'du' won't be executed at all (presuming you don't have a directory called 'bogusdir', that is ;-)). If you'd have used ';' instead, the 'du' would have been executed.

The classical example to demonstrate the difference between ';' and '&&', and the usefulness of command queuing in general, is the compilation and installation of the Linux kernel.
To compile and install the Linux, you need to execute a set of commands one after the other: 'make dep', 'make clean', 'make bzImage', 'make modules', 'make modules_install' and 'make install'. It would be annoying to issue one command, wait till it has completed, then run the next, wait, the next, wait and so on. On the other hand every command in this row will only work correctly if all the previous commands completed with no errors. If you use ';' to queue these commands and one of the commands fails, the rest will be executed regardless, and you might end up with an faulty kernel image in the '/boot' directory. So use '&&':

make dep && make clean && make bzImage && make modules && make modules_install && make install

This will compile the kernel and its modules, install them, without requiring any form of interaction.

Command Jobbing

When you run a command or start a program from a terminal, that terminal is blocked as long as the command or program is running. In Unix, we say that command or program is running in the 'foreground'. If you need a terminal to run another command, you have to open a new one.

But there's a more elegant way, called 'jobbing' or 'backgrounding'. When you 'job' or 'background' a command, it will release the terminal immediately, so that terminal can be used for new input right away. To do this, all you have to do is to put an & behind the command:

gqview &

tells the shell to execute the graphical viewer 'GQview' in the 'background' (i.e. to run it as a 'job').
The jobs command tells you which commands and programs are running as jobs on this terminal window:

jobs

[1]+ Running gqview &

This is important when you consider to close a terminal window, because upon closing the terminal all jobs running on it are terminated, that is if I would close this terminal, the GQview program started from it would be closed, too.

But what if you already have a foreground program running and want it to run in the background instead? No problem:

gqview
<CTRL z>
[2]+ Stopped gqview
bg
[2]+ gqview &

The key combination <CTRL z> suspends a program running on that terminal, so you can now use the bg command to send it to the background.

Notice that there is a situation where starting a graphical application from a terminal in the foreground is useful. This way all error messages this application issues are printed to the terminal, and while they might not be of use for you, they can provide useful hints of what's going wrong to the person you ask for help when in trouble.

Some graphical programs, usually in their testing (or Beta) period, still send messages to their controlling terminal even if they have been backgrounded. If you are annoyed by this behavior, you can turn it off with

command &>/dev/null &

This sends the program not only to the background, it also sends all terminal output to the '/dev/null' file. '/dev/null' is the system's data shredder, everything you send there promptly vanishes without a trace.

Command Substitution

Command substitution is a very handy feature. Let's say you want to have a look at the 'README.mouse' file from the XFree86 documentation, but you don't know where it is. Since you are a clever user, you've already heard of the 'locate' command and have the 'slocate' package installed. So you do a:

locate README.mouse

and you find out it is located in '/usr/X11R6/lib/X11/doc'. Now you either use the terminal pager 'less' or your file manager to display that file, providing that path. Command substitution makes that much easier for you:

less $(locate README.mouse)

accomplishes the same in one step. The output of the command 'locate README.mouse' (= /usr/X11R6/lib/X11/doc/README.mouse) is used as the argument for the 'less' pager, which now displays this file.

The syntax for this mechanism is:

command1 $(command2)

Instead of '$()', you can use single backquotes:

command1 `command2`

That's less to type, but harder to read in scripts and more easily to confuse with 'normal' single quotes, which the shell won't accept as substitution indicators. I prefer the first method, but in the end it's up to you.

Here's another example. Let's say you want to kill some stubborn program called 'rob'. You can do this by finding out what its Process ID is with the 'pidof' command and then issuing the 'kill' command with this PID as its argument. Instead of typing

pidof rob
567
kill 567

you type

kill `pidof rob`

It is more efficient, isn't it?

Filename Globbing

Filename globbing allows you to provide more than one filename to a command without having to write all filenames in full. You use special characters for this, called 'wildcards'.

Say you want to delete all files in a directory that end with the string '.bak' using the 'rm' command'. Instead of typing each filename as an argument to 'rm', you use the '*' wildcard:

rm *.bak

'*' matches zero or more characters. In this example, you tell the shell to expand the argument to the 'rm' command to "all file names ending on or consisting of the string '.bak'", the shell does so and passes that expanded argument to the 'rm' command.

As you will see, it is important to note that the shell reads and interprets the command line before the command does it. This has the advantage that you can use wildcards with (almost) all shell commands which take strings (file names, directory names, search strings etc) for an argument.

Let's play a bit more with the '*' wildcard. You have a directory which contains the files '124.bak', '346.bak' and '583.bak'. You want to keep '583.bak'. What you do is this

rm *4*.bak

The shell expands '*4*.bak' to "all file names which contain the number '4' and the end on the string '.bak'".
Notice that rm 4*.bak would not have worked, since this would only have encompassed file names beginning with the number '4'. Since there are no such files in this directory, the shell expands this pattern to an empty string and 'rm' issues an error message:

rm: cannot remove `4*.bak': No such file or directory

Now you want to keep the file '346.bak' but delete '124.bak' and '583.bak'. That's trickier since the files which are to be deleted have nothing in common except the ending. But lucky as you are, you can also define files by what they not have:

rm *[!6].bak

This reads: delete all files which end on the string '.bak' except for those which end on the string '6.bak'. You have to put the negation sign '!' and the character to be negated (here '6') into brackets, because otherwise the shell interprets the exclamation mark as the beginning of a history substitution . Negation works with all globbing patterns introduced in this article.

Notice that it is very easy to shoot yourself in the foot with the '*' wildcard and negation. Guess what

rm *[!6]*.bak

does? It deletes all the files, even the one which does contain a '6' in its filename. If you put '*' wildcards before and after a negation, it renders the negation practically useless, because the shell expands this to "all file names which do not have that character at any given position". In our example, the only file name on which that pattern would not have matched, would have been '666.bak'.

The second wildcard is the question mark, '?'. In a globbing pattern, a question mark represents exactly one character. To demonstrate its use, let's add two new files to the three example files, '311.bak~' and 'some.text'. Now list all files, which have exactly four characters after the dot:

ls *.????

does this. The question mark wildcard is also a useful means to avoid the 'negation trap' mentioned above:

rm *[!4]?.*

This expands to "all files except for those with a '4' in the second to last position before the dot" and deletes all files except for '346.bak'.

Is there more? You bet. So far, you've only seen globbing patterns which match one character at a certain position. But nothing keeps you from matching more than one:

ls [13]*

lists all files which either begin with the character '1' or the character '3'; in our test case the file '124.bak', '311.bak~' and '346.bak' match. Notice that you have to enclose the pattern in brackets, otherwise the pattern would match only files which begin with the string '13'.

Now all that's left for ultimate happiness to ensue is the possibility to define ranges of matches:

ls *[3-8]?.*

lists all files whose second to last character before the dot is a number between '3' and '8'. In our example, this matches the files '346.bak' and '583.bak'.

Quoting Special Shell Characters

These powerful mechanisms have one drawback, though: the shell will always try to expand them, and it will do so before the command. There are several cases in which this can get in your way:

You can quote such special characters like !, $, ? or the empty space either with a back slash:

ls \!*
!56.bak

or with (single) quotes

ls '!'*
!56.bak

Notice that using quotes may need some deliberation on where to put them. ls '!*' would look for file called '!*' since the second wildcard is now quoted, too, and thus interpreted literally.

Output Redirection

The Unix philosophy is to have many small programs, each excelling at a certain task. Complex tasks are not fulfilled by complex programs but by tying together a bunch of programs with a handful of shell mechanisms. One of them is redirecting output.

Redirecting between two or more commands

This is done via 'pipes', denoted by the pipe symbol |. The syntax is

command1 | command2 | command3 etc

You've certainly seen them already. They are often used to direct the output of a program to a pager like 'more' or 'less'.

ls -l | less

The first command provides the directory listing and the second displays it in a scrollable manner. A more complex example:

rpm -qa | grep ^x | less

The first command puts together a list of all installed RPMs, the second filters ('grep') those that start ('^') with an 'x' and the third displays the results in a paged and scrollable list.

Redirecting from or into files

Sometimes you want to save the output of a command in a file or feed it from a file. This is done via the operators '>' and '<'.

command > file

saves the output of command in file overwriting all previous content of file:

ls > dirlist

saves the listing of the current directory to a file called 'dirlist'.

command < file

uses file as the input for command:

sort < dirlist > sdirlist

feeds the content of 'dirlist' to the 'sort' command, which sorts it and puts the sorted output into the 'sdirlist' file. Of course, if you're clever, you'd do that in one step:

ls | sort > sdirlist

A special case is 'command 2> file'. This puts just the error messages of command into file. You may need that from time to time...
Another operator is '>>'. This one appends the output to a existent file:

echo "string" >> file

This would append string to the content of the file file. A quick way to edit a file without opening an editor first!
There is an important restriction to the '<' and '>' operators, though: Something like

command < file1 > file1

will erase the content of file1. However

command < file1 >> file1

will work fine and append the processed content of file1 to the same file.

That's a lot isn't it? ;-) No need to panic, you can learn everything step by step at your own pace. Practice makes perfect ...
Once you are familiar with the most common shell mechanisms, you might feel the urge to customize your environment.

bash Configuration Files

When you do a

ls .bash*

in your home directory, you will see a list of files:

Notice that there's a difference between the last two: '.bash_profile' is read once at the beginning of a session, whereas '.bashrc' is read every time you open a new terminal (e.g. a new xterm window). In a traditional setup you would define variables like PATH in '.bash_profile', and things like aliases and functions in '.bashrc'. But since '.bash_profile' usually is pre-configured to read the content of '.bashrc' anyway, you might as well save some effort and put all your configuration into '.bashrc'.

These files define per user settings. System wide settings are stored in '/etc/profile', '/etc/bashrc' and the files in '/etc/profile.d'. You should prefer using the per user configuration files, though: editing them doesn't require you to be 'root', and they also allow you to set up things differently for 'root' and each user account, which can be a good thing. In case of conflicts between user settings and system settings, user settings prevail.

The Prompt

The prompt is the first thing you see every time you open a console or an xterm. It looks like this:

account@hostname ~ $

In its default setting it shows your user name, the hostname of your machine (or 'localhost' is you haven't assigned one) and the current working directory ('~' is the Unix shortcut for your home directory).
Traditionally, the last character is set up to indicate whether you are a user ($) or 'root', in which case it changes to a hash (#).

You can set or change your prompt via changing the content of the $PS1 variable. The command

echo $PS1

displays the current setting. Available special characters and their meaning are listed in man bash, section PROMPTING.

Need some ideas on what might be a better prompt? Well, for starters the default setting isn't very friendly to forgetful people, since it only shows the last part of your current path. If you see a prompt like

tom@localhost bin $

your current working directory could be '/bin', '/usr/bin', '/usr/local/bin' or '/usr/X11R6/bin'. Sure, you can type

pwd ('print working directory')

to find out where you are, but shouldn't there be a way to tell the shell to do that on its own accord?
There is. The appearance of the prompt - and for most of the other settings I am going to discuss here - is set in '/etc/bashrc'. You may also change it on a per user basis by editing '.bash_profile' and '.bashrc' in your home directory.

Parameters are described in man bash, chapter 'PROMPTING'. You can add nifty things like the current time in different formats or the history number of the command, even different colors are possible.

My currently favored setting in '/etc/bashrc' is this:

case $USER in
root*)
PS1="\[\033[0;31m\][\w]\[\033[0m\] "
;;
*)
PS1="\[\033[1m\][\w]\[\033[0m\] "
;;
esac

And the prompt I get, is this:

[/usr/bin]

And this when I'm 'root':

[/usr/bin]

I've chopped the host- and user name part, since I have no need for it. But I want to see at first glance if I am logged in as a user or as 'root' on this console. Note that the user prompt will be white on dark backgrounds and black on light ones.
A more moderate setting might be

PS1="\u: \w\\$ "

which will result in prompts like these:

user_name: /usr/bin$

but who cares about being moderate? :-)

You can test various settings on the fly by using the export command (e.g. export PS1="\u: \w\\$ "). If you've found a prompt command which suits you, put it into your '.bashrc'. That way it will be applied automatically to every console or terminal window you open.

You may even 'theme' your prompt, i.e. use different colors or make it look like an good ol' C64 prompt. If you are interested in this, have a look at Bashish (the themes are shown in this section of X Themes Org).

Changing $PATH

'$PATH', like '$PS1', belongs to the group of environment variables. Type

set

to get a full list of all currently defined environment variables.
The environment variables you see here are defined in the shell's configuration files, either by the user in his/hers set of shell configuration files or system-wide by 'root' via the shell configuration files in '/etc'. If you are on X, some more variables are set by the start-up files of X and your window manager or desktop environment.

You shouldn't temper with the settings of most these variables unless you know what you are doing and why. Knowing how to change the $PATH variable, however, can be useful, since it determines the names of directories where the shell looks for commands and programs, i.e. for executable files. If the command you want to run is in a directory which is listed in $PATH, you do not need to supply the full path to that command, just the command name. Some third-party software does not install its executable files into the standard Linux directories for commands and programs. Adding their non-standard installation locations to the $PATH is a possible workaround. Furthermore it will also teach you how to deal with environment variables in general.

First off you have noticed that the names of environment variables are written in all capital letters. This is just a convention, but since Linux discriminates between capital and small letters, it is important to keep that in mind. You can define a variable like '$path' or '$pAtH', but the shell won't use it.
The second thing is that variable names are sometimes preceded by an '$', sometimes not. To set a variable, you use its name without a preceding '$':

PATH=/usr/bin:/bin:/usr/local/bin:/usr/X11R6/bin

to access the variable and its value, you need to put the '$' in front of it:

echo $PATH
/usr/bin:/bin:/usr/local/bin:/usr/X11R6/bin

because otherwise, the variable name is treated as a simple text string:

echo PATH
PATH

The third item is especially important for dealing with the $PATH variable. You can not only replace variables, but also just add new strings to their values. Most of the time, you don't want to do something like 'PATH=/some/directory' because that would erase all the other directories from your $PATH and force you to supply the full path name to every command you want to run on that terminal. Instead, just add it:

PATH=$PATH:/some/directory

Here PATH is set to its current value (represented by $PATH), plus one more directory.

So far, you've only set a $PATH variable for the terminal you have typed that command in. If you open a new terminal and issue a echo $PATH, you will see the old $PATH without the directory you just added. You have defined a local environment variable (restricted to the terminal you defined it in).
In order to define a global variable which will be recognized by all terminals you are going to open in this session, you need to export the local variable with the 'export' command:

export PATH=$PATH:/some/directory

If you now open a new terminal and type echo $PATH, you will see the new value of $PATH in this terminal, too. Notice, however, that the 'export' command only sets or changes variables for the terminal it is run in and terminals which are started after it has been run. Terminals which were already open will still have the old $PATH.

In order to add a directory permanently to your $PATH, just add the 'export' command line to your '.bashrc'.

Command Aliases And Shell Functions

Remembering all sorts of commands and their options and typing them each time is tedious work. Fortunately you don't have to. You can define shortcuts for frequently used commands. These shortcuts can either be defined in the relatively simple form of command aliases or in the somewhat more complex syntax of shell functions.

Command Aliases

For example, I use this command to upload my stuff to MUO:

rsync -e ssh -z -t -r -vv --progress /home/tom/web/muo/rsmuo/docs muo:/www/mandrakeuser/docs

Obviously I'd go nuts if I had to type this command line each time. So I have defined what is called an 'alias' in '~/.bashrc':

alias upmuo='rsync -e ssh -z -t -r -vv --progress /home/tom/web/muo/rsmuo/docs muo:/www/mandrakeuser/docs'

Now all I have to do to upload my stuff is to type upmuo.

The syntax for defining an alias is:

alias shortcut='command'

The quotes are necessary when the command contains empty spaces (e.g. between command and option). Notice that you either quote using single quotes or using double quotes. If you've got a hitch that there's a difference between those two, then you are right ;-).

Single quotes strip the special meaning from all characters included, double quotes from all characters except for '$' (parameter substitution) and '`' (command substitution). Which means in order to use variables or command substitution in aliases, you have to use double quotes. If you look at the example above, I could define a variable called MUOHOME in '.bashrc':

export MUOHOME=$HOME/web/muo/rsmuo/docs

To use that variable in the alias above, I would have to use double quotes:

alias upmuo="rsync -e ssh -z -t -r -vv --progress $MUOHOME muo:/www/mandrakeuser/docs"

because otherwise the alias would search for an directory or file called '$MUOHOME'.

You can create aliases 'on the fly' with the 'alias' command on the command line, or list them in '~/.bashrc' (per user), or in '/etc/profile.d/alias.sh' (for every user and 'root'), in pre-8 Mandrake Linux releases, '/etc/bashrc' fulfills that function. To delete an alias, simply type: unalias alias. Just running alias will list all the defined aliases on your system.

If you have a look at '~/.bashrc' and '/etc/profile.d/alias.sh', you'll see that there are already some aliases defined. You can define more than one alias for the same command. Of course, you must make sure that your alias isn't the name of some other program, something like alias rm='ls -l' won't work. You can try this by typing the shortcut you want to use on the command line. If the shell can't find a command with this name, you can use it as an alias.

Some aliases that might be useful (don't forget the quotes!):

One mnemonic hint: try to start aliases with a similar function always with the same letter. For instance, begin all your directory aliases with a 'd'.

I am sure you will find lots of possibilities for using this feature.

Shell Functions

Writing shell functions already boarders on the topic of shell scripting, which is beyond the scope of this article (and mine ;-)). In fact, shell functions are shell scripts, but they have the advantage of being preloaded and being executed in the same shell (whereas a shell script opens at least one sub-shell).

With shell functions, you can do a lot of things you can't do with aliases. One example:

function apros() { apropos $1 | egrep -v '(3|\(n\)'; }

This defines a new command, called 'apros'. apros name will execute 'apropos name' (i.e. a search command for man pages) and pipe (|) the output of that command through 'egrep' which filters out all man pages from sections '3' and 'n', which usually are not of interest, but tend to mess up the output of the 'apropos' command.
Functions allow you to use arguments given to the function name at any place of the function command. With aliases, only one argument is allowed and that argument has to be at the end of the command line (like in the 'rpmq' alias above).
'$1' is a so-called 'positional parameter', it's a placeholder for the first argument given to the function. Of course there are more.

function apros() { apropos $1 | egrep -v "\($2"; }

If you now run the 'apros' command like this:

apros name man_section_number

it searches for name, but excludes all man pages from man_section_number:

apros menu 3

returns all man page titles which contain 'menu', except those from section 3 (programming). Notice that you have to quote twice and that you have to use double quotes:

Tricky, ain't it? ;-).

Shell functions are handled just like aliases: put them into your '.bashrc' to have them around permanently.

Where To Go From Here

This article is but the beginning. Shell scripting can help you to automate a lot of tasks, to fix errors in scripts by others yourself and to accustom your Mandrake Linux system to your liking in (almost) every aspect. If you plan to learn one of the more complex programming languages out there, shell scripting is a good place to start because the basic concepts are similar.

The BASH Programming - Introduction HOW-TO will explain the topics covered in this article in more depth and introduce you to the world of shell programming. You can then continue with the very recommendable (and free) Advanced Bash-Scripting Guide by Mendel Cooper.

If you prefer books, I can recommend S. Veeraraghavan's 'Teach Yourself Shell Programming', Sams Publishing. In contrast, I found 'Learning the bash Shell' by Newham/Rosenblatt, O'Reilly, rather unhelpful and confusing, but maybe that's just me ;-).

Apart from that: practice, practice, practice. Read shell scripts by others and try to understand what they do, how and why. Don't run your test scripts as 'root'. Have fun.

Shell FAQ

How do I turn that &*#! beep off?

With this command:

setterm -blength 0

Why do I get bash: command: command not found?

If it isn't a typo, most likely the command you are trying to execute is located in a directory which is not part of your $PATH. Supply the full path to the command. If you are in the same directory as the command, do ./command.

Why do I get bash: command: Permission denied?

In order for a file to be executable, the execute permission must be set for the user who wants to execute the file. Do a:

chmod 755 file

If that doesn't work, read the article on permissions.

The execution bit is set, I have permissions to execute but permission is denied anyway.Why?

Check the '/etc/fstab' entry of the partition where that file is. Make sure it doesn't contain the option 'noexec'. If it contains the option 'user', the option 'exec' must be set, too.

How do I change the file listing colors?

Copy '/etc/DIR_COLORS' to your home directory and rename it to '.dir_colors'. Everything you need you'll find in that file.

I've put a script 'foo' into my '~/bin' directory, but every time I try to run it, a different command also called 'foo' is started. Why?

Have a look at your $PATH and you will see that your personal '~/bin' directory is the last or at least very close to the end of the $PATH. The shell will search the directories listed in $PATH one after the other. As soon as it finds the first matching command, it executes this command. If there is a command on your system with the same name as your script, it is likely that this command will be executed, not your script. So rename your script.


Legal: This text is covered by the GNU Free Documentation License. Standard disclaimers of warranty apply. Copyright LSTB (Tom Berger) and Mandrakesoft 1999-2002.