CYGWIN

lite


Welcome to Cygwin-Lite!
Index of Commands | Index of this Document

Cygwin-Lite is a UNIX-like command-line environment for Win9x/Me/NT4/2000. It is a subset of the complete Cygwin environment available for free from http://www.cygwin.com, which is based on the GNU (Gnu's Not Unix) tools from the Free Software Foundation. This document is a concise introduction to the basic functionality of the Cygwin environment, primarily GNU bash (Bourne Again SHell). You may want to look over this tutorial even if you are somewhat familiar with the MS-DOS Prompt or another UNIX shell.

The Basics  
bash is a user interface to a computer's operating system. Most popular operating systems today use a graphical user interface (GUI) like the Win95 Explorer or Mac Finder, but also include a command-line interface (CLI) like the MS-DOS Prompt. bash is a member of the breed of CLIs called shells that are the dominant form of user interface in UNIX systems. In 1983, the Free Software Foundation (FSF) began the GNU (Gnu's Not Unix) project to implement a complete UNIX-like operating system that would be covered by the innovative GNU General Public Licence, or "copyleft," making it free to copy and modify. The FSF intended not only to rewrite the hundreds of utilities that make up a full UNIX implementation, but to improve upon them where possible. The first widely used UNIX shell, called simply sh, was written by Steve Bourne of AT&T Bell Labs and is known as the Bourne Shell. GNU bash, the "Bourne Again SHell," is the FSF improvement on sh--the realization of a small part of that goal. Cygwin-Lite, as well as much of the Cygwin environment, is also covered by the GNU GPL.

Command-line interfaces have both advantages and drawbacks. Some of the advantages are fast access to a large number of tools and the ability to perform tasks on many files at once (sometimes called "batch processing"). A major drawback, however, is that every command must be memorized by users and typed exactly. bash eases this shortcoming by TAB-completion, one of the Free Software Foundation's major usability improvements. You may type partial names of commands or files followed by a TAB and bash will automatically complete them if possible; if not, a second TAB will show a list of choices. No choices usually means that you misspelled something or forgot which directory you're in.

Here is a quick example to demonstrate. There will be examples throughout this tutorial; I recommend that you try them out for yourself to learn by doing. If you ever get stuck in an example, Ctrl-C (^C) will exit a program or Ctrl-D will end a file. Type w followed by two TABs, which will show a list of commands or files beginning with that letter. Now type c /eTAB/pTAB and ENTER. You should see something like this:
[05:03:00]~$ w
wait	wc.exe	while
[05:03:00]~$ wc /etc/profile
     33      71     470 /etc/profile
[05:03:00]~$ 
wc stands for "word count"; the three numbers mean that the file /etc/profile has 33 lines, 71 words, and 470 characters. There will be examples throughout this tutorial; I recommend that you try them out for yourself to learn by doing. Note that bash and the rest of the Cygwin environment is always case-sensitive--a capital W followed by two TABs would not show you wc.exe. If you accidentally typed wc by itself, wc expects you to type a file by hand; Ctrl-C will exit, or you can type away and then end the file with Ctrl-D.

bash also supports extensive command-line editing features based on those found in GNU Emacs. bash keeps track of your command history; the Up cursor arrow or Ctrl-p will recall the last command, and the Down arrow or Ctrl-n will move back. You can search the history with Ctrl-R. A few movement keys are Ctrl-A to move to the beginning of the line, Ctrl-E to move to the end, and Ctrl-W to delete a word backwards. Other keys are listed in the READLINE section of the bash documentation (note that in Cygwin, the "Meta" or M- key is ALT).

Directory Structure and Mounts  
When you start bash, you are automatically in your home directory, something like /home/user/ , but which can always be abbreviated with a tilde as ~/. (Likewise, the present directory can be referred to as ./, and its parent by ../.) If you've used UNIX or Linux before, you are probably familiar with the concept of a home directory based on your user name. Cygwin determines your Windows login name by the id utility. (This is why the title bar briefly says id when you first start bash. It will always say the name of the last program executed.) You can see your own user name and full path of your home directory like this:
[05:03:00]~$ id -un
user
[05:03:00]~$ echo $HOME
/home/user
[05:03:00]~$ cygpath -w $HOME
C:\cygwin\home\user
[05:03:00]~$ 
Obviously, /home/user is not a Windows path name; cygpath is a utility that converts between Windows (-w) and UNIX (-u) path names. Assuming you installed Cygwin-Lite in C:\Cygwin, the Windows path would be C:\Cygwin\home\user. $HOME is an environment variable that always stores the full path of your home directory, and which was also created using id. The bash environment is covered in more depth later.

Since Cygwin is a UNIX-like environment, it creates a UNIX directory tree in the C:\Cygwin directory. The MS-DOS directory tree is similar to UNIX, but it is different in several important ways. The one you might notice first is a slash (/) instead of a backslash (\) as the directory separator. In bash, the backslash is instead used as an escape character to prevent bash from interpreting special characters like $,~, space, or even newline (ENTER). As in MS-DOS, you may also use double quotes to represent directory or file names with spaces.

The more important difference between the UNIX and MS-DOS directory structures is the concept of a root directory. All MS-DOS directories are associated with a drive letter (A:\, C:\, D:\, etc.), while UNIX has a single directory referred to by / called slash or root. All filesystems--hard disk, floppy disk, CD-ROM, etc.--are mounted on /. This allows more flexibility than the MS-DOS structure. Here is an example of mounting some MS-DOS directories in cygwin. The commands mkdir (make directory) and ls (list) are something like MS-DOS md and dir; mount has no equivalent.
[05:03:00]~$ mkdir /c
[05:03:00]~$ mount "C:" /c
[05:03:00]~$ ls /c/*BAT
/c/AUTOEXEC.BAT
[05:03:00]~$ ls /c/Program\ Files/Accessories/
CIS.SCP 	HyperTerminal	PPPMENU.SCP
SLIP.SCP	SLIPMENU.SCP	WORDPAD.EXE
[05:03:00]~$ mkdir /floppy
[05:03:00]~$ mount "A:" /floppy
[05:03:00]~$ mkdir /Desktop
[05:03:00]~$ mount "$WINDIR/Desktop" /Desktop
[05:03:00]~$ ls /Desktop
All Your Base Are Belong to Us.swf	CD Player.lnk
Phone Numbers.TXT 			Winamp.lnk
[05:03:00]~$ mkdir ~/docs
[05:03:00]~$ mount "C:\My Documents" ~/docs
[05:03:00]~$ mount
Device              Directory           Type         Flags
C:\WINDOWS\Desktop  /home/user/Desktop  user         textmode
C:\My Documents     /home/user/docs     user         textmode
C:\cygwin\bin       /usr/bin            system       textmode
C:\cygwin\lib       /usr/lib            system       textmode
C:\cygwin           /                   system       textmode
C:                  /c                  user         textmode
A:                  /a                  user         textmode
[05:03:00]~$ 
mount with no arguments lists all mounted filesystems. Any mounts you add will stay mounted after you exit just like /, /usr/bin, and /usr/lib, which Cygwin mounts automatically. If directory does not exist when you try to mount something on it, mount will warn you; umount it and then create the directory.

Moving Around 
You probably don't want to do all of your work while in your home directory (~/), so it would be helpful to know how to move around. If you've experimented, you've probably found that cd is the "change directory" command in Cygwin as well as MS-DOS. bash cd can be used like the one in MS-DOS; simply type cd followed by the path name of the directory you want to go to. However, bash cd also has several other features. cd by itself will return you to ~/. This might get frustrating if you had typed a long path name to get somewhere and then accidentally typed cd by itself--except that bash provides cd -, a "go to last directory." If you ever become confused about where you are, you can use pwd (present working directory) for a full path name.
[05:03:00]~$ cd /usr/share/vim/syntax
[05:03:00]/usr/share/vim/syntax$ cd
[05:03:00]~$ pwd
/home/user
[05:03:00]~$ cd -
/usr/share/vim/syntax
[05:03:00]/usr/share/vim/syntax$ cd ..
[05:03:00]/usr/share/vim$ ls
syntax 	tutor
[05:03:00]/usr/share/vim$ cd -
/usr/share/vim/syntax
[05:03:00]/usr/share/vim/syntax$ cd ../tutor
[05:03:00]/usr/share/vim/tutor$ 
Unlike ls or mount, cd and pwd are bash built-in commands. If you add C:\Cygwin\bin to your MS-DOS %PATH%, which is usually set in autoexec.bat, you can use the Cygwin tools from a MS-DOS Prompt, but bash built-ins must be used in bash.

Moving and Removing Files 
bash cannot move, copy, or remove files on its own; keeping with the UNIX philosophy of dedicated tools that must be combined for bigger tasks, this functionality is accomplished by the utilities mv, cp, and rm. These and other executable files can be found in the /bin directory. bash will expand wildcard characters like ? (any one character) or * (and number of any characters) as multiple arguments to these utilities.

cp must always be followed by at least two file or directory name arguments. The final argument is the destination; to copy to the present directory, use ./ as the destination. cp can take as many arguments as you want. By default, directories are not copied, but they will be if you use cp -r (recursive for files) or cp -R (recursive for directories, copy entire tree). mv likewise takes two file or directory name arguments, but moves file(s) instead of copying. Unlike cp, mv moves directories by default. rm is like MS-DOS del, but there is no undelete or Recycle Bin. rm also has -r and -R options, making it the most dangerous command in Cygwin. Using rm in combination with rmdir is usually a better idea than using rm -r.
[05:03:00]~$ cp /etc/profile profile
[05:03:00]~$ ls
Desktop	 docs	 profile
[05:03:00]~$ cp -r /etc ./
[05:03:00]~$ ls
Desktop	 docs	 etc	 profile
[05:03:00]~$ ls etc/
profile	 profile.d
[05:03:00]~$ rm e*/p*
rm: etc/profile.d: is a directory
[05:03:00]~$ rmdir /etc/profile.d
[05:03:00]~$ rmdir /etc
[05:03:00]~$ ls
Desktop	 docs	 profile
[05:03:00]~$ rm
rm: too few arguments
Try `rm --help' for more information.
[05:03:00]~$ rm --help
Usage: rm [OPTION]... FILE...
Remove (unlink) the FILE(s).

  -d, --directory       unlink directory, even if non-empty (super-user only)
  -f, --force           ignore nonexistent files, never prompt
  -i, --interactive     prompt before any removal
  -r, -R, --recursive   remove the contents of directories recursively
  -v, --verbose         explain what is being done
      --help            display this help and exit
      --version         output version information and exit

Report bugs to fileutils-bugs@gnu.ai.mit.edu
[05:03:00]~$ 
Each of the GNU tools will output an error message if you omit required arguments, and each also take an option --help that will output a summary of options and what they mean. bash also has a built-in called help that displays information about built-ins. help help summarizes the command. At times the help information from some tool or bash will more than fill the screen; on MS-DOS, you could pipe this output to more. The Free Software Foundation developed an improved version of more called less (yet another FSF pun). less --help is very comprehensive and provides a guide to its features. Pipe output to less with a command like help | less. You can reach the less help page when piping by typing an h. When reading the help, keep in mind that GNU tools are used on a wide range of different systems, so not all options may apply to Cygwin.

Working with Standard Files and Redirection  
The MS-DOS Prompt allows you to perform relatively simple tasks from the command line and slightly more complicated tasks using batch files. As you are probably beginning to realize, though, bash, especially when combined with other command-line tools, is a powerful environment that rivals some programming languages. This functionality is based on simple text files. While text files do not sound powerful, this section will explain how to use them to accomplish many tasks.

One of the foundational principles of UNIX is that "everything is a file." In shells like bash your interaction is through three special files: Standard Output (STDOUT) for regular program output, Standard Error (STDERR) for program error output, and Standard Input (STDIN) for input. These files provide a very simple way to interface different tools. You can use the redirection operators |, <, >, 2>, &>, and >> to change the way bash would normally treat these files. We have already used piping with less; the | sends STDOUT from one program to STDIN of another. STDOUT is normally displayed to the screen; with the bash command help, this was too much for one screen. less, a pager (a program that displays text one page at a time), took this output as STDIN and produced just one page of its own STDOUT.

The other operators redirect to or from other files. < sends a file to STDIN, > sends STDOUT to a file, 2> sends STDERR (also called file descriptor 2) to a file, and &> sends both STDOUT and STDERR to the same file. Changing the > to a double >> in the output redirection commands causes the redirection to add to the end of the file instead of overwriting it. You can use this with the bash built-in echo to create your own file and then view it will cat (conCATenate file(s) to STDOUT).
[05:03:00]~$ echo "This is a file" > myfile
[05:03:00]~$ cat myfile
This is a file
[05:03:00]~$ echo "This is the second line" >> myfile
[05:03:00]~$ cat myfile
This is a file
This is the second line
[05:03:00]~$ cp > myfile
cp: missing file arguments
Try `cp --help' for more information.
[05:03:00]~$ cat myfile
[05:03:00]~$ cp 2> myfile
[05:03:00]~$ cat myfile
cp: missing file arguments
Try `cp --help' for more information.
[05:03:00]~$ echo "This is a file" > myfile
[05:03:00]~$ cat myfile
This is a file
[05:03:00]~$ 
If the output from cat became too much for one screen, we could have used less instead. less acts the same whether its input comes from STDIN (as from a pipe or redirection) or a file argument. The only example of STDERR output we've seen so far is trying to invoke file utilities with no arguments. The > operator did not redirect any output, but it still overwrote the file myfile.

Redirecting STDOUT or STDERR is more useful with utilities that send output to both, such as grep (the General Regular Expression Parser). grep searches through files using regular expressions (patterns matching characters), and, like the file utilities, takes at least two arguments. The first argument is the pattern to search for, followed by at least one file to search through. Often you would like to search through every file in a directory, and so you issue a command like grep "This" *. grep can only search files, not directories, but "*" matches both. When grep finds a match, it outputs the name of file and matching line, but also sends output to STDERR when it comes to a directory. It is often helpful to separate STDOUT and STDERR in these cases.
[05:03:00]~$ grep "This" *
grep: Desktop: Is a directory
grep: docs: Is a directory
grep: etc: Is a directory
myfile:This is a file
[05:03:00]~$ grep "This" * > grep_STDOUT
grep: Desktop: Is a directory
grep: docs: Is a directory
grep: etc: Is a directory
[05:03:00]~$ grep "This" * 2> grep_STDERR
grep_STDOUT:myfile:This is a file
myfile:This is a file
[05:03:00]~$ cat grep_STDERR
grep: Desktop: Is a directory
grep: docs: Is a directory
grep: etc: Is a directory
[05:03:00]~$ 

Subshells and Job Control 
Occasionally you might want to use the output of one program as only part of the input to another program, such as copying all files that grep matches into a directory. If you try using a pipe, cp will complain that the last argument is not a directory, but piping doesn't allow you to add an extra argument at the end. You can redirect the output of one command to a file and then turn that file into a shell script, but that requires extra typing and creates messy temporary files.

The solution to this problem is treating bash itself as a utility using a subshell. The idea of a subshell is related to the concept of STDOUT. Just as you can run multiple copies of bash at the same time in different windows or consoles, each with its own STDOUT, STDERR, and STDIN, you can run a second copy of bash from within another bash session. Commands for the subshell are enclosed with the syntax $(commands). There is an older subshell representation that surrounds subshell commands with backquotes (e.g., `commands`), but since that appears confusingly similar to single quotes in shell scripts the $(...) syntax is preferred.

Subshell STDOUT is taken as STDIN to your current shell, while subshell STDERR is added to the STDERR of your shell. This means that subshell output must be either commands or arguments to commands. Therefore, to copy all files matching a certain pattern the subshell output should be a list of file names, which can be used as arguments to cp; GNU grep provides a -l option that lists only files names.
[05:03:00]~$ mkdir mydir
[05:03:00]~$ cp $(grep "This" *) mydir/
grep: Desktop: Is a directory
grep: docs: Is a directory
grep: etc: Is a directory
grep: mydir: Is a directory
[05:03:00]~$ ls mydir/
grep_STDOUT  myfile
[05:03:00]~$ 
Subshells can be very useful in building complicated commands, but keep in mind that each $(...) requires a separate copy of bash. More than one in a single command usually means a shell script would be a better choice. Like $(...) commands, shell scripts create subshells, but are more versatile.

In the last example, if grep had been searching through and then cp had been copying hundreds of large files it could have taken quite some time to finish. You could start another shell, or just wait, but bash also offers a form of multitasking called job control. When you start a program, it will normally take control from bash. This is fine for short programs that complete quickly or for interactive programs that require input, but not for possibly time-consuming tasks like searching files. bash allows you to put these jobs in the background. The easiest way to do this is to put an & at the end of the command (e.g. cp ~/largefiles/* ~/newdir/ &). bash outputs a job number followed by a process ID. For now, you can ignore the process ID (the difference is that bash assigns job numbers, while the operating system assigns process IDs).

Unlike subshells, background jobs run in the same copy of bash while you perform other tasks. Whenever a backgroud job completes, bash will output a notice with the job number, exit status, and command. Though the job is in the background, it still shares STDOUT and STDERR; you may want to redirect the output to a log file. If you accidentally forget to redirect output, Ctrl-C won't work; you can end a background job with the bash built-in kill followed by the job number with a percent sign (e.g., kill %1). Since STDIN is a different file from STDOUT, this will work even if the job is filling the screen with output and you can't see what you're typing.

A good candidate for this is the find command, which searches a directory tree by criteria and outputs the names of files that match. For example, find /c -name "*" will output the names of all the files on the C: drive, or find ~/ -mtime -1 will output all files in your home directory that have been modified in the last 1 day. find is a good command to run in the background, but if the output is not saved to a file it can quickly fill the screen. Try find /c -name "*" &, then end it with kill %1. Here is an example of logging the output to a file:
[05:03:00]~$ find /c -name "*" >findlog &
[1] 1019475
[05:03:00]~$ 
[1]+  Done                 	find /c -name "*" > findlog &
[05:03:00]~$ 

There is also job control for interactive programs that take typed input such as an editing program or a less session. Instead of being put in the background, which would make little sense for a program that expects interaction, these jobs can be suspended with Ctrl-Z (^Z). bash will output a message with the job number, the message Stopped, and the command. You can put stopped jobs in the background with bg, but be careful doing this with interactive programs; they may not behave as you expect.

You can come back to a background or stopped job by bringing it to the foreground with fg. However, stopped jobs take precedence over background ones, so fg will bring the most recently used stopped job if there are both background and stopped jobs. The jobs built-in command will show all active jobs. The most recent job is marked with a + next to its job number, and the job previous to that with a -. fg %- brings the second most recently used job to the foreground. You may also pick a specific job to bring to the foreground with fg followed by a percent sign and the job number (e.g., fg %3).

Here is a somewhat unlikely scenario to demonstrate. vimtutor is an interactive guide to the vim text editor, and the -exec option to find executes a command on found files ({} represents the file name, and /; ends the -exec command).
[05:03:00]~$ vimtutor
[1]+  Stopped 			vimtutor
[05:03:00]~$ find /c -name "*" &>findlog &
[3] 937861
[05:03:00]~$ find /c -name "*" -exec grep "qz" {} \; &>greplog &
[2] 948965
[05:03:00]~$ jobs
[1]+  Stopped                 vimtutor
[2]   Running                 find /c -name "*" >&findlog &
[3]-  Running                 find /c -name "*" -exec grep "qz" {} \; >&greplog &
[05:03:00]~$ fg
[2]   Exit 1                  find /c -name "*" >&findlog &
[3]-  Done                    find /c -name "*" -exec grep "qz" {} \; >&greplog &
[05:03:00]~$ 
First, vimtutor is started and then suspended. The find searches /c (the C: drive) for all files (files named anything, actually) and stores the results in a file findlog. Finally, another identical find also greps every file for the letters qz and stores the results in greplog. The user then returns to vimtutor and eventually exists. After some time, bash reports that the jobs have completed.

An Introduction to Shell Scripting  
The UNIX philosophy of interfacing several tools to accomplish a task can be annoying when you find yourself typing complicated commands several times. Shell scripts, which are like MS-DOS batch files but far more powerful, provide the answer to this problem. The scripting capabilities of the Bourne shell are one of the main reasons for its popularity; bash expands on sh while remaining compatible. In many cases, shell scripts act exactly like executable programs, or, more accurately, make the shell behave as if it were a different executable program. For example, the vimtutor program is really a shell script that copies the tutor text file to a temporary location and then starts vim.

Say you often want to use grep recursively as in the above example. First we need to assign any changing parameters involved to variables. The parameters to grep and find above are the search string and directory, which will be command-line arguments to the script. As in MS-DOS batch files, variables given from the command line as arguments are referred to by numbers; all variables in bash are marked with a $ (dollar sign), so the script name is $0, the first argument is $1, the second $2, etc. We can position these parameters so that the script acts just like normal grep: the search string followed by what to search. We can now build a short shell script. With echo, single-quotes are necessary to correctly print double quotation marks. With the -type f argument, find will report only files, not directories. See TESTS in the find documentation for more details.
[05:03:00]~$ echo 'find $2 -name "*" -type f -exec grep $1 {} \;' > grepr
[05:03:00]~$ cat grepr
find $2 -name "*" -type f -exec grep $1 {} \;
[05:03:00]~$ ./grepr "This" ~/
/home/user/myfile:This is a file
/home/user/grep_STDOUT:myfile:This is a file
[05:03:00]~$ 
The output of grepr (grep recursive) is the same as that from grep, except that grep now gives the full path names of the files. The only obvious difference between grep and grepr is that grepr is preceded by ./ which is necessary because by default bash will not execute files in the present directory. This is for security reasons; for example, if a password-stealing program in the present directory was called ls, a user might execute it without realizing it. To avoid having to type ./, move useful scripts to a directory in bash's $PATH (see The bash environment).

Like subshells, shell scripts are run separate from your interactive bash session. However, you can choose to use a program other than bash with a special comment of the form #!/bin/sh on the first line of the shell script. /bin/sh can be replaced with the path to any script interpreter (like /bin/bash) if needed. Comments are marked with # (pound sign) in bash; it will ignore any commands that come after a #. You can type comments to yourself at any time in bash, like ls #listing files now, but they are most useful in clarifying shell scripts.

Why would you want to use a different program to run a shell script? In some cases, the script is written in a language bash cannot understand (like perl), but more often it is for portability and efficiency. Virtually every UNIX system has a shell named sh that is compatible with the Bourne shell. (The Cygwin sh is ash, which is a good low-memory shell.) If you write shell scripts using #!/bin/sh, that script will run on UNIX systems like IBM AIX, Sun Solaris, or Compaq Tru64 that may or may not have bash installed. More pragmatically, sh is a smaller program (especially since it does not have interactive features like TAB-completion or command-line editing) and so takes less memory and will run faster. On the other hand, bash has many shell scripting features that sh does not.

So, while grepr is a useful short script, there are several problems with it. It does not specify a command interpreter, nor does it give usage information, so if you don't use it very often you may forget what it does. While comments would probably clarify the commands in the script, it would be better if grepr would tell you how to use it, just as grep does. This can be done with a compound command (a command that usually take more than one line), the if statement. From this point on, you will need to write your shell script with a text editor; it is possible to use MS-DOS EDIT.COM, but the included vim editor has many more features and, although difficult at first, is worth learning to use. vim is "Vi IMproved"; like sh, a version of vi is found on virtually every UNIX; if you ever need to work on an unfamiliar UNIX, you can rely on vi as a text editor. Run vimtutor for an introduction.

Usually the easiest way to find out how to use a command is to type its name with no arguments, as grep does. We can use the if statement with the $# variable, which reports the number of arguments, to check if no arguments were supplied. if statements begin with a test condition; we want to know if the number of arguments is less than one. The bash built-in test, which can be written as [ ] for readability, supplies the comparison -lt for "less than." [ ] is followed by a semicolon and the control word then. If the test condition is true, the commands following then will be executed; if not, bash will resume execution after the control word fi (if backwards). The command we want to add will output usage information. Here is the script as it should be typed in your text editor (note that test is picky about spaces) and some comments, followed by a comparison with the behavior of the real grep:

#!/bin/sh
# greps files recursively through a directory tree
if [ $# -lt 1 ]; then				#check if user provided 
	echo "Usage: $0 PATTERN [DIRECTORY]"	#  enough arguments, otherwise
fi						#  print usage
find $2 -name "*" -type f | xargs grep $1
[05:03:00]~$ ./grepr
Usage: ./grepr PATTERN [DIRECTORY]
[05:03:00]~$ grep
Usage: grep [OPTION]... PATTERN [FILE]...
Try `grep --help' for more information.
[05:03:00]~$ 
bash provides methods for adding options such as --help to scripts, but options for grepr would be rather silly since it has only one line of commands!

Occasionally you may find that a shell script needs to use the same few commands several times. You could place these commands in a separate script, but bash provides functions to simplify scripts. Functions begin with the word function followed by a name and an opening brace ({). After the commands, a line with a closing brace (}) ends the function. Functions, like shell scripts, are a named list of commands, but are executed in the same shell, not a separate one. bash accomplishes this by "memorizing" the function (putting it in memory), but not executing it until the function is called by name.

This improves one of the most interesting and confusing uses of shell scripts: recursion (a script executing a copy of itself). Normally, recursion with shell scripts is extremely inefficient since a new shell is required for each copy of the script. With functions, however, the lines of the function are simply copied within the shell. As an example, consider the following script, which imitates the basic find. The for compound statement will set a variable to each item in a list successively (you do not use $ with variable names when defining them); * expands to a list of all the files in one directory. for does not give a path name or recurse, but the $PWD variable (present working directory) provides the path, and we can use an if in a function to recurse if the file is a directory. At the end, showfiles cleans up after itself by removing the no longer needed variable i with unset.

#!/bin/bash
function showfiles {
for i in *; do
	if [ -e $i ]; then 	#test if any files exist (empty directories)
		echo $PWD/$i
	fi
	if [ -d $i ]; then	#test for directory, call self if true
		cd $i
		showfiles
		cd ..
	fi
done
unset i
}

showfiles
Note the recursive call right after changing directories in the second if. Also, the function is defined, but not executed until the last line, which is simply showfiles, a call to the function. You can probably imagine a few basic improvements to this script, such as allowing an argument for where to start searching; experiment with adding more functionality.

Defining a function is analogous to writing a shell script such as grepr, while actually calling it is like typing the command. Like shell scripts, functions can also take arguments. If you're wondering if there is a way to access functions from the bash prompt like in a shell script, there is: the source command. source is a bash built-in command that copies lines from a file exactly and executes them one at a time, just as if they had been typed by the user. You can create a file that defines one or more functions but never calls them, like showfiles above would be without the last line, and then put the functions in bash's memory with the command source function_file, where they are available from the command line and completed with TAB-completion. Sourcing files is so common that its abbreviation is simply . (dot), so assuming you put the showfiles function definition (without the call on the last line) in a file named showfiles you can have bash memorize it with the command . ./showfiles.

The bash environment  
It was briefly mentioned in an earlier section that bash sets environment variables like $HOME, which is used to determine your home directory, automatically. $PWD, used in the function example above, is another of bash's environment variables, which by convention are written with all capital letters. bash uses these variables in various commands. For example, recall from the Moving Around section that cd without arguments will take you to your home directory, while cd - will return you to the directory you were in previously. These commands actually change the directory to the values in $HOME and $OLDPWD respectively. You can set the value with a command of the form OLDPWD=/path (remember not to use the $ when setting variable values):
[05:03:00]~$ cd /etc
[05:03:00]/etc$ cd
[05:03:00]~$ echo $OLDPWD
/etc
[05:03:00]~$ OLDPWD=/usr/share/vim
[05:03:00]~$ cd -
/usr/share/vim
[05:03:00]~$ echo $OLDPWD
/home/user
[05:03:00]/usr/share/vim$ cd
[05:03:00]~$ 
The cd - command should have returned you to /etc, but took you to /usr/share/vim since $OLDPWD was redefined.

It's best to not change system-defined variables like $USER or $HOME unless there is some problem with your user name (for example, spaces in a user name can be confusing and difficult to type), but other bash environment variables are customizable. To see the names of all environment variables, you can type echo $ followed by two tabs, which will list all defined variables. One of the most important of these is $PATH which is a colon-separated list of directories that bash searches for commands. As mentioned earlier, the current directory is not in the path, so you need to type ./ to execute a files there.

You can redefine $PATH to include your home directory with a command like PATH=$PATH:$HOME, but since variables are stored internally, the redefined variable will only last until you exit bash. So how does bash set variables like $PATH, $HOME, or $USER in the first place? The secret is in the bash system initialization file, /etc/profile. Every time bash starts with the --login option or from a login prompt, it automatically sources the commands in this file. /etc/profile contains lines like PATH="/usr/local/bin:/usr/bin:/bin:$PATH", USER="`id -un`", andHOME="/home/$USER". Notice that the file is written with old-style `id -un` instead of $(id -un).

You may have also noticed a line like export HOME USER in /etc/profile. The built-in command export makes a variable available to subshells. The idea behind export is that not all variables are important, and so only important ones need to be given to subshells or shell scripts. You can see all current exports by typing export with no arguments; some of those in Cygwin ($COMSPEC, $WINDIR) are left over from the Windows environment files and so follow the MS-DOS path naming convention but are listed with a double backslash in bash.

Since bash is designed for multi-user systems, each user has a personal initialization file that is sourced like /etc/profile is for the whole system, ~/.bash_profile. Additionally, there is a startup file ~/.bashrc that is sourced for all interactive shells. This allows you to put a command that you only want to see the first time you start bash, such as echo "Welcome, $USER" in .bash_profile. These configuration files are hidden files in your home directory. In UNIX, hidden files are simply ones that begin with a . (dot), which are only shown by commands such as ls when the -a (all) option is used. Hidden configuration files are sometimes called rc files since they often end with the letters rc ("run commands").

To see how the sourcing works, you can put lines that echo information, like echo "This is $HOME/.bash_profile" in ~/.bash_profile. When you start bash, you would see something like this:
This is /etc/profile
This is /home/user/.bashrc
This is /home/user/.bash_profile
This is /home/user/.bashrc
[05:03:00]~$ 
Why does .bashrc appear twice? The last line of /etc/profile explicitly sources .bashrc with the command test -f ./.bashrc && . ./.bashrc so that the file will be sourced even if the login shell is not interactive. This command is the same as:
if [ -f $HOME/.bashrc ]; then
    source $HOME/.bashrc
fi
As usual, the less readable form takes less typing. The distinction between .bash_profile and .bashrc is important in some circumstances, but it is common to leave .bash_profile only a few lines long and put nearly all definitions in .bashrc. Keep in mind that any changes you make will normally not take effect until you restart bash. You can refresh .bashrc with the command exec bash, which will start a new interactive shell, or simply source the files.

Several of the customizations previously mentioned would be good to put in your .bashrc. If you write several shell scripts, you could create a ~/bin directory and add it to your path with the line PATH=$PATH:$HOME/bin (make sure to include the old path, or bash will complain that it can't find commands like ls, which are in the /bin directory). You can also define useful functions or source files with functions in them.

Another fun to customize environment variable is the prompt bash outputs when it is ready to read a command. You may have noticed that /etc/profile exports a variable called PS1, which is "prompt string 1," the default prompt. In Cygwin-Lite, PS1 is something like [05:03:00]~/$ , the current time followed by the present working directory and a $. Besides regular characters, you can use several special escaped characters bash provides to build your own PS1. A few of these are \t (the current time), \w (the present working directory), \u (user), \! (how many commands are in your history), and \[...\] for control sequences like changing colors; others are listed in the bash documentation under PROMPTING. A prompt like [05:03:00]~/$ would be set as PS1="[\t]\w\$ ".

Aliases are another type of environment customization besides variables and functions to put in .bashrc. An alias is a word that you want bash to replace when it is entered as a command. You can create an alias with a command like alias md='mkdir'. Now, if you type md newdir, bash will translate it to mkdir newdir before executing it. Aliases are best put in .bashrc since they are never exported. If there is more than one possible source for a command, for example an alias and function with the same name, bash's preference goes in this order: alias, function, built-in, then program like a shell script or executable.

Another rc file that effects bash is .inputrc, which controls the READLINE command-line editing features that bash and other programs use. You can use .inputrc to customize command-line editing. Say two aspects of bash's command-line editing bother you: the bell sound every time you use TAB-completion, and the lack of a keyboard shortcut to exit like most graphical application have. You can create a file in ~/ called .inputrc and add the following lines:
set bell-style none
"\M-q": "exit\n"
The first turns off the bell, and the second binds ALT-q (remember ALT is the META key in Cygwin) to exit followed by a newline (\n), which is what you type to quit. You can bind any text in this way, but keep in mind that you may be overwriting a default value. (You can see all bound keys in the .inputrc format with the bind -p command, which bind -P outputs in a slightly more readable way.)


Other Resources 

This guide covers only the basics of using and customizing bash. The best place to find more information or help is on the Internet; here are a few places to start.

Online

Information on bash
Bash Reference Manual - Table of Contents
The official Free Software Foundation (FSF) manual for bash. It may be freely downloaded and redistributed provided the copyright is preserved.
Linux Documentation Project (LDP)
The LDP contains a vast array of information on all aspects of GNU/Linux systems. Like the FSF documentation, all LDP documents can be freely downloaded and redistributed provided copyright is preserved. Since bash is the default shell for GNU systems, GNU/Linux web sites, newsgroups, and IRC channels are good places to look for help. A few useful HOWTOs at the LDP are:
Bash Prompt HOWTO
Information about customizing your user environment.
BASH Programming - Introduction HOW-TO
An introduction to shell scripting with bash
Advanced Bash-Scripting HOWTO
More advanced bash shell scripting.
Learning Debian GNU/Linux by Bill McCarty
(O'Reilly, 1999, 360 pages, ISBN 1-56592-705-2)
While most of this book (now out of print, but the full text is still available online) deals with aspects of GNU/Linux that do not apply to Cygwin, portions of two chapters provide an alternative short guide to bash. These sections would be a good place to start if this guide moved too quickly.
Chapter 4: Issuing Linux Commands - How Linux Organizes Data
Covers the filesystem layout and moving around in the shell.
Chapter 13: Conquering the BASH Shell
Covers environment customization and basic shell scripting.
General Information
Cygwin-Lite Website
This project's website, where updated information can be found.
GNU Manuals Online
Official Free Software Foundation manuals for all GNU software. It is organized by packages. You can determine the specific package a utility can be found in (and its version number) with the --version option (e.g., du --version outputs du (GNU fileutils) 3.16). The Cygwin-Lite HTML documentation sidebar is organized by the same packages.
Cygwin User's Guide
The official manual for the full Cygwin environment. Documentation for files in the cygwin package can be found here.
Introduction to UNIX (1996)
A guide to the UNIX operating system originally written as lecture notes for a college class at Ohio State University. Includes a concise introduction to shell scripting with the original Bourne shell.
Rute Users Tutorial and Exposition
An extremely extensive guide to running and administering GNU/Linux systems. Parts apply to Cygwin.

Books

Learning the bash Shell, 2nd Edition by Cameron Newham & Bill Rosenblatt
(O'Reilly, 1998, 336 pages, ISBN 1-56592-347-2) Currently US$29.95
A carefully-paced introduction to all the significant features of bash, both as an interactive shell and a programming environment. Includes a shell-script debugger.
UNIX Power Tools, 2nd Edition by Jerry Peek, Tim O'Reilly, & Mike Loukides
(O'Reilly, 1997, 1120 pages, ISBN 1-56592-260-3)
A useful reference for UNIX users, including sections on bash and other shells and GNU tools.


Index of this Document
Introduction
The Basics
Directory Structure and Mounts
Moving Around
Moving and Removing Files
Working with Standard Files and Redirection
Subshells and Job Control
An Introduction to Shell Scripting
The bash environment
Other Resources

Copyright (C) 2001 Joshua Daniel Franklin
Cygwin-Lite is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.