Chapter 3: Working with files and directories

Table of contents

Chapter 3

Working with files and directories

This chapter discusses files and directories and the basics of working at the command prompt. It describes how to:

Getting to the command prompt

The commands described in this book are all intended to be typed at a command prompt in a text display (or window, if you are using a graphical terminal).

There are several ways of getting to a command prompt, depending on how your system is configured. In general, you must first log in to the system, then (if you are using a graphical environment) open a shell window.

If you are using the graphical environment:

  1. Log in, as described in ``Starting the Desktop'' in the SCO OpenServer Handbook.

  2. Open a UNIX window by double-clicking on the UNIX icon, located by default on the Desktop. Otherwise, select UNIX from the Tools menu.
If you are working on a character terminal:

  1. Log in, as described in ``Logging in'' in the Operating System Tutorial.

  2. If your login automatically starts up the SCO Shell, type:

    !ksh<Enter>

You should now see a ``$'' or ``%'' symbol with the cursor positioned next to it. This is the command prompt: you are now ready to start entering commands.

Note that the examples in this chapter assume that you are running the Korn shell. If you are not sure which shell you are running, see ``Identifying your login shell''. If you are not running the Korn shell, type:

ksh<Enter>

This starts a temporary Korn shell session.

If you have not used the command prompt before, you may want to refer to Chapter 1, ``Getting started'' in the Operating System Tutorial before continuing with this chapter.

Files and directories

The basic unit in which the SCO OpenServer system stores information is the file. A file is a named collection of data that you can move around, copy, rename, or delete. Files are stored in a filesystem, a storage area on your computer's hard disk or disks. A filesystem is split into directories, which are smaller storage areas that make it easier to locate individual files.

Using files

The system is unconcerned with the structure of the information in a file; all it sees is a stream of characters. Individual programs may impose a structure on the file, and you may see references to records and fields within a file, but the file itself is the smallest piece of information that is stored under a name and recognized by the system.

Using directories

A typical system contains many files, perhaps tens of thousands. To keep track of them, they are divided into directories. A directory is an area of the filesystem that is assigned a name; it can contain files and, optionally, directories. By using the name of a directory instead of the name of a file as the parameter of a command, you can make the command operate on all the files stored in that directory simultaneously. The first, top level, directory on the system is called the root directory; all the other directories and files in the system trace their ancestry back to it. 

Files belonging to a particular user are usually stored in that user's own directory; those associated with a single project or application are also often stored in a single directory. Users can also create directories within their home directory to store files relating to specific projects. The operating system looks after the organization of system files, but you are responsible for the organization of your own files.

File and directory attributes

The system handles files and directories in the same way; directories are just specialized files, containing other files and directories rather than program code or text. Files and directories both have a name, a path, and a set of attributes. Internally, the system keeps track of files and directories using inodes, or index nodes. See ``How the system manages files and directories''.

A simple way of checking some of the attributes of a file or directory is the long listing, obtained using the -l option to the ls(C) command (or just l):

What you see in a long listing

How the system manages files and directories

Internally, the system keeps track of files and directories using inodes. An inode (or index node) is a representation of a file that stores all the data belonging to that file, such as owner, type, size, access permissions, access times and the file's layout on disk. Each inode has a unique number which is used by the system in file handling operations: the filename is simply a device to make the filesystem easier to use for humans. In fact, while a file may have only one inode number, it may have several filenames, these being links to the one inode. See ``Creating links to files and directories'' for more information on links to files and directories.

Filenaming conventions

The maximum permitted length of a file or directory name is 255 characters. In fact, this is controlled by the value of the {NAME_MAX} constant; to check the value of this, use the getconf(C) command, as follows:

   $ getconf NAME_MAX .
   255
Pathnames, which are described in ``How directories are organized'', have a maximum permitted length of 1024 characters (as controlled by {PATH_MAX}, which is also controllable using getconf).

An important consideration, where Open Systems are an issue, is filename portability. Many of the international standards specify a character set that should be used for the construction of portable filenames. The IEEE POSIX standard, for example, specifies the following Portable Filename Character Set:

Portable filenames should not begin with a hyphen, although it may appear in any other position. Specifically, the following characters should not be used in file or directory names because they have a special meaning for the UNIX system:

! " ` ' ; / $ < > ( ) | { } [ ] ~

The slash character (/) signifies both the root directory and the pathname element separator, and is valid only in these contexts. See ``How directories are organized'' for more information on pathnames.

Filenames may begin with a dot (.), but this has the effect of excluding them from normal directory listings. See ``Listing the contents of a directory'' for details of how to list these ``hidden'' files.

Managing directories

Together with files, directories constitute the UNIX filesystem, which has a characteristic inverted tree structure beginning at the root directory. As with files (see ``Managing files''), you can create, rename, copy, erase, and compare directories. You can also set access permissions on directories to prevent or enable their use by other users. This is explained in ``Access control for files and directories''.

How directories are organized

Directories are organized as an inverted tree structure. Only one directory, at the top of the tree, is not contained in any other directory. This is called the root directory, and its name is represented by a slash (/) character. To help clarify this, look at the following picture of part of a directory tree:

In this picture, directories are depicted by ovals, and ordinary files by rectangles. Notice that most of the directories have lines coming out of them, indicating that they lead to files or other directories.

When you work within a UNIX filesystem, you always have a current working directory. All filenames and commands that you type at the prompt are evaluated with respect to this position. When you log in, your current working directory is set to the directory created for your user account. This location is known as your home directory.

Any file or directory on the system can be specified uniquely by its pathname. Pathnames are instructions for finding a file or directory; they list, in order, each directory you must pass through to get to the file or directory in question. When the pathname is written down, the directories are separated by slashes (/). For example, the full pathname of the file called review in the diagram above is /usr/susanna/review. The first slash character denotes the root directory: all the others are separators.

A pathname that begins at the root directory is called an absolute or full pathname. There are also relative pathnames, which give directions to a file relative to your current working directory. Two dots in a row (..) represent the parent of the current directory. 

For example, if you were working in the directory called /usr/sarah and you needed to specify the file in /usr/susanna called mbox, you could use the relative pathname ../susanna/mbox, or the absolute pathname /usr/susanna/mbox. (Remember, ``..'' refers to the parent directory of the current working directory; so in this case it refers to /usr, which is the parent of both susanna and sarah.)

An example: what the system contains

When an SCO OpenServer system is installed, many directories are created automatically. The following figure shows a partial structure of a UNIX root filesystem. (A full root filesystem would be too large to show here.)



/
The root directory is the root of the filesystem tree. Every directory is a subdirectory of root.

/bin and /usr/bin
These directories contain most of the UNIX system commands. Generally, standard UNIX commands and applications are held in /bin, whereas group-specific commands and applications, that is, those used by a particular group of users, are held in /usr/bin.

/dev
This directory contains all the special device files. Special device files are access points to all the peripherals connected to the system.

/etc
This directory contains many of the system configuration files and system administration commands.

/unix
This file contains the UNIX kernel program. This program is loaded into memory when the operating system starts up. It is the heart of the SCO OpenServer system; for more information see ``The UNIX system kernel''.

/usr/lib
This directory contains many application library files.

/usr/spool
This directory is used by many commands for storing temporary files or files in a queue.

/var/opt/
This directory contains storage sections. See ``Creating a link to a directory'' for information about symbolic links and storage sections.

Creating a directory

To create a new directory, use the mkdir(C) (make directory) command, as follows:

mkdir directory

The directory argument can be either a simple name, in which case the new directory is created within the current working directory, or a pathname. For example, if you want to create a new subdirectory called projects in the directory called /u/workfiles, you can do the following:

   $ mkdir /u/workfiles/projects
You get the same result by entering the following:
   $ cd /u/workfiles
   $ mkdir projects
The cd command stands for ``change directory''. See ``Navigating the filesystem'' for more details.

For details of naming conventions for directories, see ``Filenaming conventions''.

You can create a directory within any directory where you have write permissions. See ``Access control for files and directories'' for details of how to manage access to files and directories.

You can create several directories at the same level. For example, to create directories called directory1 through directory3, use the following command:

   $ mkdir directory1 directory2 directory3
If you need to create an entire directory path at once, use the mkdir -p (path) option. The following command creates a directory called user_guide, and any of the other directories in the specified path that do not already exist:
   $ mkdir -p projects/myprojects/user_guide
Bear in mind that the efficiency of the filesystem has some impact on overall system performance. For example, you should not let your directories grow larger than necessary. Ideally, a directory should contain no more than 640 files (providing that the number of characters in each filename is 14 or less), otherwise it may take the system longer to search the directory whenever you access a file stored in it. If you use longer filenames, the limit may be lower. File size is also significant: large files impose overheads on access. See ``Looking at the contents of a file'' for more information.

Listing the contents of a directory

The names and other information about the files and subdirectories contained within a directory can be displayed using the ls(C) family of commands. In its simplest form, ls gives a list of the filenames found in the current working directory, as follows:

   $ ls
   cs-save
   gav_make
   glossary.s
   graphics
   intro.err
   nohup.out
   procs.txt
To see a list of the filenames in a multi-column format, use the lc variant, as follows:
   $ lc
   cs-save         intro.err
   gav_make        nohup.out
   glossary.s      procs.txt
   graphics
For a full listing, giving file size, permissions, owner and other items of information, use the ls -l option, as follows:
   $ ls -l
   drwx------   2 chris   techpubs      64 Jul 07 17:19 tools
   drwxr-xr-x   2 chris   techpubs      80 Jul 06 16:51 trash
   -rw-r--r--   1 chris   techpubs    6204 Sep 23 09:34 travel
For a complete breakdown of this information, see ``File and directory attributes''. In fact, this version of the command is used so commonly, that it can be entered in shorthand, as l(C).

As we saw in ``Filenaming conventions'', filenames may begin with a dot, in which case, the files are hidden from normal directory listings. The ls -a (all) option displays hidden files as well as normal files, as follows: 

   $ ls -a
   .
   ..
   .history
   .kshrc
   .mailbox
   .profile
   cs-save
   gav_make
   glossary.s
   graphics
   intro.err
   nohup.out
   procs.txt
To list the contents of another directory, without first moving to that directory, use the ls command, specifying the directory to look at as an argument, as follows:
   $ ls /u/workfiles/projects
This command line lists the contents of a directory called /u/workfiles/projects. 

You need permission to read a directory before you can view its contents. See ``Access control for files and directories'' for an explanation of permissions.

The tilde-plus sequence (~+) is expanded by the shell to point to the current working directory (actually, the value of the PWD environment variable). A more useful variant of this notation is the tilde-minus notation (~-), which expands to the value of OLDPWD, that is, the previous working directory. This allows you to refer back to your earlier work without having to type in the relevant pathname, as follows:

   $ ls -l
   drwx------   2 chris   techpubs      64 Jul 07 17:19 tools
   drwxr-xr-x   2 chris   techpubs      80 Jul 06 16:51 trash
   -rw-r--r--   1 chris   techpubs    6204 Sep 23 09:34 travel
   $ cd ../project2
   $ ls -l
   -rw-r--r--   1 chris   techpubs    3137 Oct 24 17:49 agenda
   drwxr-xr-x   2 chris   techpubs      96 Aug 31 13:08 bin
   $ ls -l ~-
   drwx------   2 chris   techpubs      64 Jul 07 17:19 tools
   drwxr-xr-x   2 chris   techpubs      80 Jul 06 16:51 trash
   -rw-r--r--   1 chris   techpubs    6204 Sep 23 09:34 travel
If you list the contents of a directory that contains more files and subdirectories than can be displayed on one screen, the list scrolls continually until all the files have been displayed. This makes it very difficult to read them. To view the list one screen at a time, type the following:
   $ ls | more
The output from ls is piped to the more(C) command which then displays it; more prints its input one screen at a time. (See ``Running commands in a pipeline'' for more information about pipes.) Press <Enter> to scroll down by one line, or the <Space> bar to scroll down by one screen. Otherwise, you can pipe the output from ls into the pg(C) command, which performs a similar operation. The main difference between the two is that pg allows you to step backward through a file (by pressing the minus key (-)), as well as forward (by pressing <Enter> or the plus key (+)).

Another way to pause the scrolling is to use the <Ctrl>S and <Ctrl>Q keystrokes. Press <Ctrl>S to temporarily stop the scrolling, and <Ctrl>Q to continue. If you want to stop the listing completely, press <Del>. These keystrokes depend on your terminal setup; if they do not seem to work, ask your system administrator to help you.

Renaming a directory

To rename or move a directory, use the mv(C) (move) command, as follows:

mv oldname newname

oldname is the directory's current name, and newname is the new name you want to assign it. As with the mkdir command, arguments to mv may be simple names or pathnames, as follows:

   $ mv users_guide uguide
   $ mv appendix ../docset2/admin_guide/app
The first of these commands renames a subdirectory: the second renames a directory from appendix to app, at the same time moving it to another location.

Copying a directory

To copy all the files in a directory, use the copy(C) command, as follows:

copy old_directory new_directory

old_directory is the name of the directory you want to copy, and new_directory is the name you want the copy to have. Simple names and pathnames are acceptable in both cases.

In order to copy a directory and all its files, you must have read and execute permissions on that directory, read permission on the files in that directory, and write permission on the directory into which you want to copy. See ``Access control for files and directories'' for details.

Removing a directory

To remove an empty directory, use the rmdir(C) command, as follows:

rmdir directory

This will fail if there are any files or subdirectories in the directory. If this is so, you must either delete the files it contains, or move them to other directories. The only exceptions are the dot and dot-dot directories, which you cannot delete, and which are dealt with by the UNIX system itself.

You can remove a directory and any files it contains by using the rm -r option, as follows:

rm -r directory

Be very careful when doing this because the -r option tells rm to recursively enter any subdirectories and remove their contents. You may remove more than you expect. It is often safer to use the rm -i option. See ``Removing a file'' for information on interactive deletion. You must have write permission on a directory before you can remove it.

Comparing directories

Comparing directories is useful when two people are working on the same set of files, for example, the chapters of a book. By comparing the directories you can quickly identify which files are different.

To compare two directories, use the dircmp(C) (directory compare) command, as follows:

dircmp directory1 directory2

The output is a list of the differences between the directory listings for directory1 and directory2, for example:

   $ dircmp ./dir1 ./dir2 
   Jan 12 10:54 1995  dir1 only and dir2 only Page 1
   

./t.appx.s ./t.prog.s ./t.scosh.s ./t.shell.s . . . Jan 12 10:54 1995 Comparison of dir1 dir2 Page 1

directory . same ./0.ct.s same ./00.partno.s same ./00.title.s . . .

The top of the listing consists of those files which are unique to one or other of the directories; in this example, dir2 contains four files which are not present in dir1. The listing then contains a detailed comparison of every file which is named in both directories.

Navigating the filesystem

The following sections explain how to find out where you are in the directory tree, how to move from directory to directory, and how to list the contents of a directory.

Finding out where you are in the system

After a number of cd operations, it is possible to lose track of where you are in the filesystem. To identify your current directory, use the pwd(C) (print working directory) command. This command takes no arguments. 

The output from pwd shows the absolute pathname of your current directory. For example, if your login is johnd and you are in your home directory (which is a subdirectory of /usr), the output would probably look like the following:

   $ pwd
   /usr/johnd

If you are using the Korn shell (see ``Identifying your login shell'' if you are unsure about this), you will find it useful to issue the following command as soon as you log in, or add it to your login script:

   alias pwd='pwd -P'
The reason for this is explained in ``Navigating symbolic links''.

Changing directory

Once a directory system exists, you need to know how to get from one directory to another, thereby changing your current working directory. This is done using the cd(C) (change directory) command. As an argument to the command, you specify the directory you want to change to, as follows:

cd directory

For example, if your current directory is /u/johnd, you can to change to a directory called /u/workfiles/projects by specifying its absolute pathname, as follows:

   $ cd /u/workfiles/projects
You can also change to the directory by specifying its relative pathname, as follows:
   $ cd ../workfiles/projects
Note that you must have execute permission on a directory before you can change to it. See ``Access control for files and directories''. See ``Returning to your home directory'' for details of a special usage of cd.

Returning to your home directory

You can return to your home directory from anywhere in the directory structure by typing cd on its own.

Your home directory is stored in the HOME environment variable. You can display this value using the following command line:

   $ echo $HOME
Remember to include the dollar sign in the command line: without it, the echo(C) command will simply return the word ``HOME''. For an explanation of environment variables, see ``Understanding variables''. For more on the echo command, see ``Forcing a program to read standard input and output''.

A useful tool for filesystem navigation is the tilde character (~). The shell expands a tilde to the absolute pathname of your home directory. This notation can be included in a pathname, as follows:

   $ pwd
   /tmp
   $ cd ~/i486/dev/backup/scripts
   $ pwd
   /usr/martins/i486/dev/backup/scripts
Because the pathname is absolute, you do not need to know where it is in relation to your current working directory.

The tilde can also be used in conjunction with other users' login names, acting as a shorthand way of accessing their files without necessarily knowing exactly where their home directories are located. For example, the following command line allows you to check a colleague's directories for any file called hyacinth:

   $ find ~john -follow -name hyacinth -print
(See ``Finding files'' for how to use the find command.)

Creating links to files and directories

You may need to make a file accessible from more than one directory, and by more than one user, but still keep it as a single file. This is often the case when you need to share the data in a file with your colleagues. To prevent different versions of the file from proliferating, only one copy of the file exists, but links are created that allow you and your colleagues to access the file from your home directories or another convenient location. (Note that there is the danger of a file becoming corrupted if more than one person tries to edit it at the same time.)

Creating a link to a file

To create a link to a file, use the ln(C) command, as follows:

ln filename linkname

For example, suppose you have a file called user_guide which is located in /u/workgrp/tasks/projects. To work on this file you would normally cd to that directory before opening the file. However, by creating a link to the file, you can access it from your current directory (without needing to enter the full path of the file). To do this, type the following:

   $ ln /u/workgrp/tasks/projects/user_guide my_guide
The my_guide argument identifies the link. Whenever you want to work on the file, which is now known to the system by two names (user_guide and my_guide), you can access it from the current directory by using my_guide as the filename.

You must have write permission on a directory before you can create a link that involves that directory or a file in that directory. You cannot create a hard link (the kind of link described above) to a directory or a file on a different filesystem. To create a link to a directory or a different filesystem, you must use a symbolic link. See ``Creating a link to a directory'' for details.

Links can be removed using rm. If a file has several links, it is not physically deleted until the the final link is removed.

Finding out whether a file has hard links

To find out how many hard links there are to a file, use the ls -l or l (long listing) command, as follows:

   $ l
   -rw-r--r--   1 johnd   unixdoc    10586 Feb 25 12:26 1.start
   -rw-rw-r--   2 johnd   techpubs   61339 Feb 24 14:45 2.scosh
   -rw-rw-r--   1 johnd   techpubs   14741 Feb 25 11:18 3.dire
   -rw-rw-r--   3 johnd   techpubs   40419 Feb 25 15:57 4.files
You need to locate all the links to a file or directory if you want to delete it: as long as there are links, you cannot delete the file or directory.

In a long listing, the number of links are shown in the second column from the left (after the sets of permissions). For example, in the above example, the file 4.files has three links to it.

You can find out where the common links to a single file are located, in two steps. First, you need to identify the inode number of the file (see ``How the system manages files and directories'' for information on inodes). To do this, use the ls -i option, as follows:

   $ ls -i 
   20350 basking_shark
    3886 cod
    2002 halibut
    3526 herring
   10182 narwhal
The number before the filename is the file's inode number, for example, 2002 for the file called halibut.

To trace all the links to this file, you must find all the other files in the filesystem with this inode number. You can do this using the find -inum option, as follows:

   $ find / -inum 2002 -print 2>/dev/null 
   /u/dave/tmp/halibut
   /u/charles/fish6
   /u/michael/project2/ichthyo14
In this case, there are three files with the same inode number. In order to delete this file from the filesystem, it would be necessary to run rm on all the links.

Creating a link to a directory

It is often useful to change to another directory without typing its full pathname: symbolic links provide a useful shortcut to do this. A symbolic link differs from a hard link. It is a small file that contains a reference (by name) to a directory or file that already exists. Unlike normal links, symbolic links can cross filesystems and link to directories. (They are used extensively by the system.) Also unlike normal links, symbolic links are separate files; they cease to work if the file they point to is deleted or renamed, or if they are moved.

Many of the files found in /bin, /lib, and /usr are actually symbolic links that point to files (of the same name) stored below /var/opt. The directories these files are located in are called ``storage sections''. Storage sections are used because they make it easier to install system upgrades. Software subsystems (such as UUCP) consist of many files, which may be installed in several directories. However, all the files in a subsystem belong to a single storage section. By overwriting the contents of the (single) storage section directory, all the files in the subsystem can be updated simultaneously.

Symbolic links are identified in a directory listing by a ``->'', as follows:

   $ l
   drw-r--r--  1 johnd  unixdoc     29  Feb 27 15:56 mydata -> /u/work
   group/tasks/project/01 
You can obtain a directory listing without symbolic links visible in it by specifying the -L (logical) option. This makes ls (or l, or any related program) list the directory, replacing the information about each symbolic link with the details for the file pointed to by the link:
   $ l -L
   drw-r--r--  1 johnd  unixdoc   10297  Feb 27 15:56 mydata
To create a symbolic link, use the ln -s option, as follows:
   ln -s directory symbolic_link
For example, suppose you work in /u/workgrp/tasks/projects and your home directory is /u/me. Your normal command to work on a file would be the following:
   $ cd /u/workgrp/tasks/projects
To reduce the typing required, enter the following command:
   $ ln -s /u/workgrp/tasks/projects mydata
This command creates a symbolic link called mydata in your current directory. From now on, mydata and /u/workgrp/tasks/projects refer to the same location, and you can relocate to /u/workgrp/tasks/projects by typing cd mydata instead of typing in the full pathname.

You must have write permission on a directory before you can create a link that involves that directory or a file in that directory.

See also ``Access control for files and directories''.

If you remove a symbolic link, only the link itself is removed. If you remove (or move) the directory or file to which the link points, the link will be left pointing to nothing.

Navigating symbolic links

Because the system uses symbolic links extensively, you may encounter problems in identifying your current working directory. For example, suppose you create a symbolic link to a directory, then change directory using the new link, as follows:

   $ ln -s /u/workgrp/tasks/projects mydata
   $ pwd
   /u/people/mike
   $ cd mydata
   $ pwd
   /u/people/mike/mydata
   $
In this example, you create a symbolic link called mydata, pointing to /u/workgrp/tasks/projects. However, if you change directory via the link mydata, you actually see yourself as being in /u/people/mike/mydata. This is the logical present working directory; that is, the path the user traversed to reach the directory. The directory also has a physical path, that is, the actual pathname of the current directory, relative to the top of the filesystem. 

Now you are in /u/workgrp/tasks/projects. Suppose you create another symbolic link and change directory into it:

   $ ln -s /u/people people
   $ pwd
   /u/people/mike/mydata
   $ cd people
   $ pwd
   /u/people/mike/mydata/people
   $
When you change directory into people, you are following a link to /u/people. This is higher in the directory tree than your original starting point, but because you are traversing another symbolic link, your logical present working directory is another level down the tree. 


If you then type cd .. (to go up a directory), where you end up depends on your shell. If you are running the Korn shell, the cd .. command goes up a level in your logical directory path: /u/people/mike/mydata becomes your present working directory. If you are running any other shell, the cd .. command goes up a level in your physical directory path: /u becomes the present working directory.

Despite the apparent complexity, it is possible to determine your physical working directory in a directory tree populated with symbolic links. There are two techniques, for Korn shell users and for others:

Korn shell users
Use the command pwd -P to identify your absolute current working directory. (The command pwd is built into the Korn shell. It normally returns the logical present working directory; the -P option makes pwd return the physical path to the current directory.

others
Use the command pwd(C). The non-Korn shells do not include a built-in pwd command. The external pwd command returns the physical path to the current directory by default.
In general, directory traversal commands built into the Korn shell accept two options, -P and -L. -P makes the command refer to the physical working directory, while -L makes the command refer to the logical working directory (that is, to the path taken through any symbolic links).

The equivalent commands, in any other shell, always apply to the physical working directory.

Mounting a filesystem

In addition to containing loose files, a storage medium may be used to store a whole filesystem, with its own root directory and subdirectories. A filesystem stored in this way can be mounted onto the main filesystem using the mnt(C) command.

Once a filesystem has been mounted on another filesystem, its root directory appears as a subdirectory of the parent filesystem. You can enter the mounted filesystem by using cd, and it looks just like another subdirectory hierarchy, even though it resides on another physical device.

Mounting a filesystem

To mount a CD-ROM filesystem, for example, you must first place the disk in a CD-ROM drive installed on your computer, then use the mnt command. For example:

   $ mnt /mnt
The /mnt directory is a mount point; when a device is mounted on it that device's root directory appears in the filesystem instead of /mnt. Any files that existed in /mnt before the new filesystem was mounted on it are obscured, although they will be accessible again when the filesystem is unmounted.

Once the CD-ROM is mounted, it appears on the system as a tree of subdirectories, with the root directory of the CD-ROM located in the mount directory /mnt.

The mnt command reads a file called /etc/default/filesys (see filesys(F)), which contains a list of mountable filesystems. This file also specifies the name of the device associated with the filesystem (the bdev keyword) and the absolute pathname of the filesystem's mount point within the parent filesystem (the mountdir keyword).

Your system administrator must have added an entry to this file before you can use mnt to mount a CD-ROM. You can examine the contents of /etc/default/filesys with the mnt -t option, as follows: 

   $ mnt -t
   Mount Directory:                 /apps
   Block Device:                    /dev/apps
   Character Device:                /dev/rapps
   Password required:               No
   Mount if requested:              No
   Fsck if requested:               Only if filesystem is dirty
      Fsck flags:                   -y
   Mount at system startup:         Yes
   Fsck at system startup:          Only if filesystem is dirty
      Fsck options:                 -y
   Run command before mounting:     No
   Run command after mounting:      No
   

Mount Directory: /private Block Device: /dev/private Character Device: /dev/rprivate Password required: No Mount if requested: Yes Fsck if requested: Only if filesystem is dirty Fsck flags: -y Mount at system startup: Yes Fsck at system startup: Only if filesystem is dirty Fsck options: -y Run command before mounting: No Run command after mounting: No

Otherwise, use cat(C) to show the contents of /etc/default/filesys, as follows:
   $ cat /etc/default/filesys
   .
   .
   .
   bdev=/dev/apps cdev=/dev/rapps \
           mountdir=/apps rcmount=yes \
           mount=no fsckflags=-y
   bdev=/dev/private cdev=/dev/rprivate \
   

mountdir=/private rcmount=yes \ mount=yes fsckflags=-y

For a filesystem to be mountable by a user other than the root user, its /etc/default/filesys entry must contain the command mount=yes. In the example, the filesystem /dev/private is mountable by users while /dev/apps is not.

To unmount a filesystem, use either the umnt(C) command, as follows, or the mnt -u option.

   $ umnt /mnt
You can also mount a DOS filesystem, allowing the use of DOS files without first copying them into the UNIX filesystem. For details, see ``Using mounted DOS filesystems''. 

Managing files

The directory handling operations (creating, deleting, administering and navigating) allow you to impose a structure on your area of the filesystem. It is the files, however, that store information like program code, text, database records and graphics. The SCO OpenServer system supplies a wide range of commands for managing files. The following sections discuss some of these. Some of the more complex systems are discussed in other chapter; see, for example, ``A quick tour of vi'' for an explanation of how to use the vi(C) editor.

Finding out what type of data a file contains

As we saw in ``File and directory attributes'', the system supports numerous different file types. The contents of text files, for example, can be displayed on the screen using such commands as cat, more and pg. Doing this with a binary (or compiled program) file, may cause the screen to lock, as such files usually contain many control characters. (See ``Looking at the contents of a file'' for more on the display commands and on garbling your screen.)

To avoid using an unsuitable command to display the contents of a file, first find out what kind of information a file contains. To do this, use the file(C) command, as follows: 

   $ file mbox 
   mbox:   ascii text
   $ file tools
   tools:  directory 
   $ file /bin/lc
   /bin/lc:        iAPX 486 executable
The file command accepts either a simple filename or a pathname as an argument.

Looking at the contents of a file

The simplest way to look at the contents of a short file is to use the cat(C) command, as follows:

cat filename

If the file is more than one screen long, it scrolls off the screen, making it difficult to read its contents. If this happens, press <Ctrl>S to temporarily stop the scrolling, and <Ctrl>Q to restart the scrolling. If you want to stop the scrolling completely, press <Del>.

If you do not know what is in a file you want to look at, use the cat -v option, as follows:

cat -v filename

This option causes any unprintable characters in filename to be displayed in a manner which does not garble your screen. If you do use cat without using the -v option, and your screen becomes garbled and the machine beeps a lot, press <Ctrl><Del>, <Break>, or <Del> (depending on your terminal). If you cannot clear it, you may need to ask your system administrator for help.

To look at the contents of a file that is too big to fit on a single screen, use the more command, as follows:

more filename

You can use the pg command in the same way.

You can look at more than one file at a time by using the display commands with several filenames as arguments, as follows:

   $ more file4 file5 file6
In the case of the more command, press <Space> to display a screenful of text. When you reach the end of the first file, more displays a message at the bottom of the screen (Next file: filename2). Press <Space> again to go to the next file.

If you want to go directly to the next file before finishing the first, enter :n; more skips to the next file. See ``Listing the contents of a directory'' for more information on the more and pg commands.

Finding out how much text is in a file

The wc(C) command counts the number of lines, words, and characters in a file, using the options -l, -w, or -c respectively. For example, to print the number of characters and lines in a file called myfile, execute the following command:

   $ wc -cl myfile
       32675  684  myfile
The order in which you specify the options determines the order of the output.

You can also give wc a list of files to count:

   $ wc chap1 chap3 
       105    676   3844 chap1
       675   3869  24269 chap3
       780   4545  28113 total
The total line gives sums for the lines, words and characters in the two files, chap1 and chap2.

Looking at the beginning and end of a file

To look at the first ten lines of a file, use the head(C) command:

head filename

To look at the last ten lines of a file, use the tail(C) command:

tail filename

If you use a numerical option, for example -20, head or tail will print that number of lines (20) instead of ten, the default, as follows:

   $ head -20 file6.txt

Copying a file

To copy one or more files, use the cp(C) command, which takes one of the following formats:

cp filename copyname
cp filename ... pathname

In the first format, filename (with optional path) is the name of the existing file that you want to be copied; copyname (with optional path) is the name you want the copy to be created with.

If you are using the second format to copy a group of files, you can only specify a directory, pathname, as the destination of the specified files. filename and copyname cannot be the same if they are both in the same directory.

When you copy a file you are creating a duplicate of it, which occupies additional space in the filesystem. Although the contents of the new file are the same as those of the original file, the new copy has its own inode number; any operations carried out on it have no impact on the original.

For example, to copy the file project1 from your current directory to the directory /u/workgrp, type the following command:

   $ cp project1 /u/workgrp
The copy will retain the name project1, but will have a different pathname.

You can copy a file to your current directory by typing a command line like the following:

   $ cp ../../a.out .
In this case, the file called a.out is located two levels above the current working directory, and is to be copied to the current location (as indicated by the ``.'' notation).


NOTE: When copying a file, be careful not to overwrite an existing file. To avoid this, do not create a copy with the same name as an existing file, as this will overwrite (clobber) the contents of the existing file. This can be avoided by setting the noclobber variable. See ``More about redirecting input and output'' for details.

When you copy a file, you automatically become its new owner. Accordingly, you must have read permission on a file before you can copy it. You can place files in any directory for which you have write permission. If you want to create a copy of a file without changing its ownership, use the command copy -o instead of cp; this preserves the owner and group of the file. For example, to copy /tmp/johnsfile to your home directory without changing the ownership of the file, type the following:

   $ copy -o /tmp/johnsfile johnsfile
For information on how you can assign the ownership of a file to someone else, see ``Giving a file to someone else'' and ``Access control for files and directories''.

Moving or renaming a file

To move one or more files to another directory, use the mv(C) command, as follows:

mv filename ... pathname

The one or more filename arguments (with optional path) specify the file or files you want to move; pathname is the path to the directory where you want to put the file.

For example, to move the file project1 from your current directory to the directory /u/workgrp, type the following:

   $ mv project1 /u/workgrp
The procedure for moving files is the same as for renaming files. You rename a file by moving it to a new filename. To move (rename) a file, type the following:

mv old_filename new_filename

old_filename is the file's current name and new_filename is the name you want to change it to.

You can move a file to a different directory and rename it at the same time. For example, the following command line moves chapter.1 to /u/workgrp and renames it to finished.chapter.one at the same time:

   $ mv chapter.1 /u/workgrp/finished.chapter.one
You can place files in any directory to which you have write permission. To move a file, you need read permission unless you own it.


NOTE: If you give a file the same name as an existing filename, the contents of the existing file are overwritten or ``clobbered''. The existing file is deleted. (You can make the system refuse to overwrite existing files by setting the noclobber variable: see ``Specifying command input and output'' for details.)

Removing a file

To remove (or destroy) a file, use the rm(C) command, as follows:

rm filename

Once a file is removed from the system, there is no way of getting it back unless a backup exists on tape or floppy disk, or the filename is a link, or versioning is available. Links are explained in ``Creating links to files and directories''; file versioning is explained in ``Retrieving deleted files''.

You can list several files to be removed, or use wildcards to select files. You cannot remove directories with this form of the rm command.


NOTE: It is potentially dangerous to use wildcards to remove files. Before doing so, you should confirm that the correct files have been selected: do so by running the ls command in place of rm. Because the expansion of any filename notation is handled by the shell and not by the individual command, the files selected by ls are the same as those that will be selected by rm.

To remove files interactively, use the -i option, as follows:

rm -i filename1 filename2 . . .

rm with the -i option asks for confirmation before removing a file. A question mark is displayed and you can either type ``y'' to remove the file, or ``n'' to not remove it. It is a good idea to use rm -i to reduce the risk of accidentally removing files. For example, to remove several files from the current directory:

   $ rm -i f*
   file1: ?y
   file2: ?y
   file3: ?y
   format.doc: ?n
As a further safeguard, it may be useful to create an alias, whereby executing rm -r * actually executes rm -ir *: the -i option causes rm to delete files interactively; that is, you must confirm the deletion of each file before it is carried out. See ``Using aliases'' for details of how to create an alias.

Note that using wildcards does not remove hidden files (those whose name begins with a dot); that is, typing rm * does not necessarily remove all the files in a directory. To list the hidden files, type ls -a. For example, if you have a file called .project, you can remove it by typing the following:

   $ rm .project
Remember that there are always at least two files that cannot be removed from a directory; ``.'' (the current directory), and ``..'' (the parent directory). 

You can remove a file from a directory other than your current one if you have write permissions on that directory. 

Removing files with difficult names

Occasionally, files are created by accident with awkward names. For example, they might contain a slash (/) or an asterisk (*) character. These files cannot be removed by normal means without the risk of destroying other files, because if you try to type their names, the shell will interpret the special characters as wildcards.

For example, suppose you have a directory that contains a corrupted file called all * file and a number of files called file1 and file2 that you want to keep. If you type rm all * file, rm will interpret the ``*'' in the filename as a wildcard, and attempt to execute the following:

rm all file1 file2 file

This command thereby inadvertently deletes file1 and file2.

To correctly remove files with corrupted names, the easiest solution is to use the rm -i option. In this case, rm will prompt you for confirmation before removing each of the specified files in the current directory; type ``n'' for each file other than the corrupt one you want to remove, as follows:

   $ rm -i a*
   all * file: ? y
Alternatively, specify the name of the file, surrounding it with single quotes:
   $ rm 'all * file'
The single quotes prevent the shell from expanding the special character ``*'' in the file's name.

If you have a file that begins with a hyphen (-), rm will mistake its name for an option of some kind. For example, if your file is called -myfile, rm -myfile will be mistaken for an invalid rm command. You can overcome this by invoking rm with the special option, --, which tells rm that the following argument is not an option:

   $ rm -- -myfile
See Chapter 12, ``Regular expressions'' for an explanation of shell wildcards. See ``Filenaming conventions'' for an explanation of what constitutes an illegal filename.

Comparing files

It is often necessary to compare the contents of two files and list any differences. This may be because you have made some changes to a file and cannot remember them; if you have a previous version of the file, you can compare the two. You may have two files with the same name in different directories; you can compare them to see if they are different files or two versions of the same file.

To see if two files differ, use the cmp(C) (compare) command which reads file1 and file2 and reports whether or not they are different: 

cmp file1 file2

If they differ, cmp reports the point at which the two files diverge. This is reported in terms of the number of characters into file1 at which the difference was detected, and the number of the line containing that character, as follows:

   $ cmp chapter4 chapter4.bak
   chapter4 chapter4.bak differ: char 28895, line 849
In this case, the two files are the same up to a point on line 849 of chapter4.

To see the precise differences on a line-by-line basis, use the diff(C) command, as follows: 

diff filename1 filename2

For example, consider the following two files, note.1 and note.2:

   $ cat note.1
   Charles
   Please send me a report.
   I need it for tomorrow's meeting.
   Thanks
   Bridget
   $ cat note.2
   Charles
   Please send me a report today.
   I need it for tomorrow's meeting.
   Thank you
   Bridget
To compare these files, line by line, use the diff command as follows:
   $ diff note.1 note.2 
    2c2
   < Please send me a report.
   ---
   > Please send me a report today.
   4c4
   < Thanks
   ---
   > Thank you
The ``2c2'' means that there is a change (``c'') between line 2 in the first file and line 2 in the second. Likewise, the ``4c4'' means that there is a change between line 4 in the first file and line 4 in the second. The ``<'' refers to a line in the first file, and ``>'' refers to a line in the second file. The ``---'' separates the output from each file.

If you want to compare three files, use the diff3(C) command. If you want to compare two sorted files, use comm(C). 

Sorting the contents of a file

You can sort a file containing lines of text or numerical data in a variety of ways using the sort(C) command. For example, suppose you have a file called names containing the following:

   perry
   john
   sarah
   charles
To sort its contents alphabetically, enter the following command:
   $ sort names
   charles
   john
   perry
   sarah
To direct the sorted output to a file (names1) rather than the screen (standard output), you can use either of the following command lines:
   $ sort -o names1 names
   $ sort names > names1
You can cause the original file to be sorted by giving the original filename for both arguments.

You can make sort merge two files together, in order. To do this, type the following:

sort filename1 filename2 > filename3

This creates filename3, which contains the sorted, merged contents of filename1 and filename2. (The sort command sorts the files as it merges them.) You can use the -u option to tell sort to make sure that each line in filename3 is unique; that is, if both filename1 and filename2 contain an identical line, only one copy of the line will be written to filename3:

   $ cat file1
   perry
   john
   sarah
   charles
   $ cat file2
   susanna
   charles
   bridget
   john
Running the sort command on these files merges the contents and places them in alphabetic order, as follows:
   $ sort -u file1 file2 >file3
   $ cat file3 
   bridget
   charles
   john
   perry
   sarah
   susanna
There are several more options that can be used with sort. For example, -r sorts in reverse order; -n sorts on numerical order, not text order; -M causes sort to assume that the first three characters of the field being sorted are months (like ``JAN'', ``FEB'', ``MAR'', and so on) and sorts them into date order.

You can make sort select any field in a line and have it base its comparisons on that field, as follows:

   $ cat birthdays
   charles FEB
   bridget DEC
   sarah   JAN
   $ sort -M +1 birthdays
   sarah   JAN
   charles FEB
   bridget DEC
The +1 flag tells sort to make comparisons between records on the basis of the second field of each line. So, the month abbreviation on each line of the file is used as the basis for the sort operation above, and not the alphabetic order of the first field.

If you have a file where data records are made up of fields separated by some special character (called a ``separator''), you can tell sort to use that separator by using the -t option, as follows: 

   $ cat birthdays
   charles:FEB
   bridget:DEC
   sarah:JAN
   $ sort -M +1 -t: birthdays
   sarah:JAN
   charles:FEB
   bridget:DEC


Searching for text in a file

To search one or more files for some text, you can use the grep(C) command, as follows:

grep options text filenames

(``grep'' is an acronym for ``global regular expression print''; for a full explanation of regular expressions, see Chapter 12, ``Regular expressions''.)

grep searches the contents of filenames for text, and prints any matches. You might want to do this if you cannot remember the name of a file in which you left some information, but can remember enough of it for grep to find it for you.

For example, you might want to locate a memo in the current directory (full of files called something.memo), when you know that the file you are looking for contains the string ``Subject''. The command to use is as follows:

   $ grep 'Subject' *.memo
   stan.memo:Subject:  That's another fine mess you've gotten us into! 
grep prints the context of any matches, line by line, with the relevant filename (where more than one file was specified for the search) followed by the line of text that contains the specified string.

The single quote (' ') marks are necessary if you want to search for a string containing spaces, tab characters, or double quote marks. Double quote (" ") marks are necessary if you want to search for a string containing single quote marks; you should put a backslash immediately in front of each quote character (\' \'), as follows:

   $ grep "I\'m right" stan.memo
   Thanks for nothing. I'm right in the center of it (or
If you are not sure whether the string is uppercase, capitalized, or all lowercase letters, use the grep -i (ignore case) option; grep ignores the case of the text in the files being searched, and report all matches, as follows:
   $ grep -i 'PhD' database.memo
   yesterday, when he was awarded his PhD in New
   not so easy to get a pHD nowadays, what with
   is it PHD, phd, phD, etc? He should have stopped
This search has found all lines containing the string ``PhD'', irrespective of how it is capitalized.

If you want to see all lines in a file that do not contain the string, use grep -v.

The use of regular expressions and pattern matching in search operations is explained in Chapter 12, ``Regular expressions''. See also regexp(M). 

If you have a file containing columns of data in textual form, you can extract information from it using a variety of tools. For example, supposing you have a file called blackbook containing names, extension numbers, login names and dates, in a format like the following:

   Michael Stand:571:mikes:JAN-1-91
   Sue Penny:284:suep:FEB-6-89
   Joshua Ford:954:joshf:JUL-30-88
   Liz Addams:553:liza:AUG-15-91
To see Sue Penny's record, use the following command:
   $ grep Sue blackbook
   Sue Penny:284:suep:FEB-6-89
This is hard to read. To see only Sue's extension number (the second field), you can use the cut(C) command, as follows:
   $ grep Sue blackbook | cut -f2 -d:
   284
The cut command extracts individual fields from a file containing records. The -f2 option tells cut to extract only the second field of each record; the -d: option means that fields are delimited with a colon. In this way, input records may contain spaces and tabs without these characters signaling the start of a new field.

The pipe (|) tells grep to send its output to another program (in this case, to cut) instead of the standard output. See ``Running commands in a pipeline'' for more information on pipes.

To see a list of all the people in your file, followed by their login names, you do not need to use grep: instead, use the cut command, as follows:

   $ cut -f1,3 -d: blackbook
The -f1,3 option tells cut to extract the first and third fields in each record:
   Michael Stand:mikes
   Sue Penny:suep
   Joshua Ford:joshf
   Liz Addams:liza
If you want to put your list in alphabetic order, you can sort it as follows:
   $ cut -f1,3 -d: blackbook | sort -df
   Joshua Ford:joshf
   Liz Addams:liza
   Michael Stand:mikes
   Sue Penny:suep
A more powerful and versatile tool for this sort of operation is the awk(C) command. See Chapter 13, ``Using awk'' for an explanation of its use.

Permanent executable copies of complex command lines like these search tools can be stored in shell script files for future use. See Chapter 11, ``Automating frequent tasks'' for details.

Finding files

To search the system for a particular file, use the find(C) command, as follows:

find start_point -follow -name filename -print

The start_point argument tells find where to start searching in the filesystem, for example, root. find searches its starting directory, and all the subdirectories. If you know your file is in one of your own subdirectories you could tell find to start searching from $HOME.) The -follow option tells find that if it encounters a symbolic link, it should follow it to the file the link points to, as described in ``Navigating symbolic links''. The -name option is followed by the name of the file you are looking for. Every time find sees a file with this name, it carries out the actions specified by the subsequent options. For example, the -print option tells find that the action it must take when it finds filename is to print its pathname:

   $ find /tmp -name myfile.tmp -print 
   /tmp/myfile.tmp
find gives lots of error messages when you do not have permission to search a directory, for example:
   $ find / -name chap3 -print
   /u/charles/stuff/os/chap3
   /u/w/Xenix/OS2.3.2/Intlsupp/Guide/chap3
   

find: cannot chdir to /etc/conf/pack.d/arp find: cannot chdir to /etc/conf/pack.d/arpproc . . .

To suppress these error messages, redirect the error output of the find command to /dev/null (the UNIX system's ``black hole'' directory), as follows: 
   $ find / -follow -name chap3 -print 2> /dev/null
For more information on redirecting output, see ``Specifying command input and output''. 


find can be used to apply a command to a collection of files that match some selection test, for example, files that are older than a specified age. You can remove all files in your home directory, and all its subdirectories, that have not been accessed for seven days by typing the following command line:

   $ find $HOME -follow -name '*' -atime +7 -exec rm {} \;
find starts from the directory specified by its first parameter (in this case, the value of $HOME), follows symbolic links, and selects all the files matching the designated name (in this case, '*') that were last accessed (-atime) seven or more days ago. It then executes (-exec) the rm command on the found file (represented in the expression by {}). The ``\;'' at the end of the line terminates the -exec expression. Note that the single quotes around the ``*'' are required. Otherwise find searches for files with names matching those matched by ``*'' in the current directory.

The -exec option allows the execution of any legal shell command along with any permitted options and arguments.

find can carry out other tasks when it finds a file. For example, the following command causes find to execute cp on any file called datafile in the directory /bin; this file is then copied to your home directory.

   $ find /bin -follow -name datafile -exec cp {} $HOME \;

Retrieving deleted files

File versioning is the ability of a system to preserve and access old copies of a file. Traditionally, the UNIX system does not support file versioning: whenever the UNIX system updates a file, the preceding image of its contents is lost. Certain editors make a copy of a file before updating it, but this feature is specific to the individual utility, and usually restricts itself to the maintenance of the current file and a single backup version. 

Keeping old versions of files

The SCO OpenServer system supports file versioning. The root user must configure it for a filesystem at the time of mounting by setting the MINVTIME and MAXVDEPTH kernel parameters. These respectively set the interval before a file is versioned and set the maximum number of versions maintained (or steps in the evolution of the file's contents). Setting these to 0 disables versioning. (See ``Versioning filesystems (undelete)'' in the System Administration Guide for details.)

When file versioning is configured, you must then enable it for a specific directory using the -s option of the undelete(C) command, as follows:

   $ undelete -s /users/mike/source/user_guide
This command line switches on versioning for all the files in the directory called user_guide and any subsequent child directories.

When file versioning is configured and enabled, it makes the filesystem preserve a copy of a file's contents whenever it is overwritten or deleted. This is done silently. The number of versions preserved depends on the setting of MAXVDEPTH. These copies can be retrieved at a later date using undelete.

You can make versioning visible by setting the SHOWVERSIONS environment variable to 1, as follows:

   $ SHOWVERSIONS=1; export SHOWVERSIONS
Within a directory, you can create versions for a single file, irrespective of whether general versioning configuration or enabling has been carried out, using the undelete -v option, as follows:
   $ undelete -v chapter3


NOTE: File versions created in this way will always be visible, independently of the value of SHOWVERSIONS. However, when the filesystem is mounted with versioning enabled, file versions created using undelete -v will not be visible.

A versioned filename has the following syntax:

filename;version

The filename element follows the normal UNIX file naming rules, and is separated from the file version by a semicolon.

To see a list of all the existing versions of a file, use the undelete -l option, as follows:

   $ undelete -l magnolia.txt
   undelete magnolia: no versions
   $ undelete -l begonia.txt
   begonia.txt;1
   begonia.txt;2
   begonia.txt;3
Specifying a version identifier causes only that version to be used in the command line; the existing versions are accessible in the same way as separate normal files:
   $ cat homework.txt\;1
   Italian Assignment 3
   

C'era una volta, l'orsetto che viveva nella foresta al ovest ... $ cat homework.txt\;2 Italian Assignment 3

Tanti anni fa, l'orsetto che vivo' nella bosca verso ponente ...

Note that the semicolon is a shell metacharacter, and must be quoted in the context of versioning, using the backslash. See Chapter 12, ``Regular expressions'' for more information on metacharacters and quoting. 

Undeleting files

On traditional UNIX systems, once you have deleted a file, you cannot retrieve it, other than by searching through any existing backup tapes. The SCO OpenServer system undelete command makes this process much easier on versioned files. These exist in three combinations:

To undelete a file that was removed by mistake, make the previous version current. Although the file itself may have been removed, the versions are still accessible, as the following sequence shows:
   $ l
   -rw-r--r--   1 sallyp   accounts    3768 Oct 31 16:03 begonia.txt
   $ rm begonia.txt
   $ l
   total 0
   $ undelete begonia.txt
   $ l
   -rw-r--r--   1 sallyp   accounts    3768 Oct 31 16:04 begonia.txt
When you specify a filename without a version identifier, you retrieve the most recent version of the file.

However, it is possible to check how many versions are available, using the undelete -l option, as follows:

   $ undelete -l
   begonia.txt;1
   begonia.txt;2
   begonia.txt;3
You can use undelete to recover any available version, as follows:

   $ undelete begonia.txt\;2
   $ l
   -rw-r--r--   1 sallyp   accounts    3768 Oct 31 16:05 begonia.txt
This silently overwrites an existing version of the same file.
   $ undelete -i begonia.txt
   begonia.txt;3: ? y
   $ l
   -rw-r--r--   1 sallyp   accounts    3768 Oct 31 16:05 begonia.txt
The undelete -i option recovers the most recent version of the specified file. If the the file already exists, you must choose whether to overwrite it. In this case, the most recent version (number 3) of the file is retrieved but the earlier versions are discarded.
   $ undelete -l undelete begonia: no versions


Cleaning up your filesystem

File versioning is a useful tool, but it can have an adverse effect on the filesystem, as unneeded old versions of files can rapidly accumulate, reducing free space. It is important to remember that switching on versioning for a directory causes it to apply to all child directories as well. Therefore, directories should be regularly cleared of unwanted old file versions.

The undelete -p option ``purges'' specified files. This permanently deletes the existing versions (but not the file itself).

   $ undelete -l
   tulip;1
   tulip;2
   tulip;3
   tulip;4
   $ undelete -p tulip
   $ undelete -l
   : no versions
Versioning is inherited by child directories, so the following command line is useful for cleaning out a whole directory system:
   $ undelete -prd /users/mike/source/user_guide
The -r option specifies a recursive purge, while -d specifies the directory at which the operation is to commence.

Another useful option is -m, which takes a number of days as an argument.

   $ undelete -m+2
   $ undelete -m-2
   $ undelete -m2
These command lines respectively cause undelete to consider only files deleted more than, less than and exactly, two days ago.

In filesystems where versioning is widespread, the issue of free space may be crucial. Therefore, it is advisable to set up a crontab(C) job containing a command line like the following, which executes a forced recursive purge of all files deleted more than two days ago, starting at the directory /users: 

   $ undelete -rpfm+2 /users
See ``Executing processes at regular intervals'' for details of the crontab command.

Specifying command input and output

Almost all UNIX commands require an input and an output; that is, some information to read and process, and somewhere to store the results. If you do not tell a command where to find its input and output, it makes assumptions about where to read and write information. These assumptions are called the standard input and standard output. These are, respectively, your keyboard and your screen by default. Alternatively, if you specify the name of a file, most programs will obtain their standard input by reading the file.

In addition to standard input and standard output, most programs need a standard error, to which they report any failures or errors. By default, the standard error is directed to the same place as the standard output, your screen.

You can make commands redirect their standard input and output by using the symbols ``<'' and ``>'' on the command line, followed by the name of a file to read input from or write output to. For example sort < list1 > list.out makes sort treat list.1 as its input, and send its output to list.out.

If you send the output of a program to a file, and the file already exists, the existing file will be ``clobbered'' or overwritten. If you are using the Korn shell, you can prevent this from happening by using the noclobber variable. You can identify your login shell by entering the following command:

   $ grep ${LOGNAME} /etc/passwd
   martins:x:13990:1014:Martin Smith:/u/martins:/bin/ksh
The last data field, after the last colon, identifies your login shell, in this case, the Korn shell (/bin/ksh). If you are using the Korn shell, you can turn on the noclobber feature, by typing the following:
   $ set -o noclobber
C shell users should type set noclobber: this feature does not exist in the Bourne shell. To turn off the feature in the Korn shell, type set +o noclobber. C shell users should type unset noclobber. To protect yourself from accidentally clobbering files, Korn shell users should add the appropriate noclobber line to your .profile file: C shell users should add it to .cshrc.

To append the output of a command onto the end of a file, use the >> notation instead of >. For example, the following command line appends the output from sort to the end of the existing contents of file2 rather than overwriting them:

   $ sort file1 >> file2
The C shell will not let you append the standard output to a file if the file does not exist and noclobber is set. The Bourne or Korn shells simply create the file.

Typically, your shell will support a wider range of redirection operators than those discussed here. For details, refer to ksh(C), sh(C) or csh(C) as appropriate. See also ``More about redirecting input and output''.

Forcing a program to read standard input and output

Many programs get their input from the standard input and write their output to the standard output: others read from or write to named files.

For example, cat can be used to create a file using information typed in at the keyboard, as follows:

   $ cat > file8
In this case, file8 is a file that does not already exist within the current directory. You can then proceed to type text into the file. You can press <Bksp> to correct any mistakes you make on the current line, although you cannot correct mistakes on previous lines. Press <Ctrl>D when you have finished, to signal ``end of file'' to cat.

Alternatively, you can use the echo command to place text in a file, as follows:

   $ echo "Hello there!" > testfile
This results in a file (testfile) containing the text ``Hello there!''.

Note that the quote marks are stripped out by the shell when you use echo to print to a file.

The following use of the cat command places the contents of the three named files into outfile:

   $ cat file1 file2 file3 >outfile
Suppose you want cat to read file1, then read something from the standard input (your terminal) instead of from file2, then read file3. There exist some special device files to make life easier: /dev/stdin, /dev/stdout, and /dev/stderr. These three special files correspond to the standard input, standard output, and standard error, respectively. The following command line uses /dev/stdin in precisely this way:
   $ cat file1 /dev/stdin file3 >outfile
In this case, file1 is copied to outfile; then cat reads the standard input (your terminal) until you press <Ctrl>D (to signal end of file); then finally appends file3 to outfile.

Running a sequence of commands

When you type a command line and press <Enter>, the entire line is evaluated as a single unit. It is possible to run several programs from one line; either sequentially, or simultaneously, or in a ``pipeline'' where the output from one command is used as input to the second command. You can also store frequently-used commands in a file, and tell the shell to execute the contents of the file as a script.

Entering commands on the same line

To send several commands, one after another, separate each of them with a semicolon. For example:

   $ ls > list ; sort list > list1
This command sequence creates a list of files in a file called list. It then sorts the contents of the file alphabetically and redirects the output into a file called list1. The command after the semicolon is not executed until the command before it has completed; the shell waits for the earlier commands to finish.

Running commands in a pipeline

A pipeline is a sequence of commands that operate concurrently on a stream of data. All the processes are started simultaneously, but instead of reading or writing to a file or terminal, they read or write to and from a pipe. As the first process begins to produce some output, that output is fed to the second process as input, so that both processes are working at the same time. For example: 

   $ ls | sort > list1 
Here, the ls command sends its output straight to sort, which processes it and sends its own output to the file list1. Unlike the similar command line in ``Entering commands on the same line'', no intermediate file called list is created. Writing to a temporary file is a comparatively slow process because it involves transferring data to disk, and the second process must then access the file and read it back into memory. Pipes, in contrast, transfer data directly from one process to another without writing it to the disk.

More than one pipe operation can appear on a single command line, as follows:

   $ sort -u file | grep basilisk | wc -l > words
This pipe sequence creates a file called words, containing a count of all the nonidentical lines in file that contain the word ``basilisk''.

Access control for files and directories

Because the SCO OpenServer system is a multiuser system, it is important that strict control is placed on file access. For example, as a user you cannot change files that belong to someone else without their authorization. Controlling access to files is achieved by use of permissions.

Every file has three sets of permissions that control who can read it, write it (that is, change it), and execute it. You can change the permissions on your own files to make them more or less accessible to other users on the system. The following is a representation of the permissions information displayed by the ls -l command. Remember that the first character position actually gives the file type, and is not a permissions indicator; see ``File and directory attributes'':


The permissions field for a file is made up of nine character positions following the file type indicator. They are divided into three sets of three permissions each; a set for the owner of the file, a set for the group of users to which the file belongs, and a set for everyone else on the system. These are respectively known as ``owner'', ``group'' and ``other''.

Note that the superuser (root) can always read or write every file on the system. This is a special privilege that is not available to any other user.

Each set of permissions can include none, one, or more than one of the following privileges:

Read
If you have read permission, you can look at the contents of a file. For a directory, this means you can see a list of the files it holds. Read permission is represented by an ``r'' in the first of the three character positions for each of the three sets of permissions, as follows:
-r--r-----   1 johnd   unixdoc    10586 Feb 25 12:26 1.start
The ``r'' in the first character position of owner's set and the group set means that the owner and members of the owner's group can read the file; nobody else is permitted to do so.

Write
If you have write permission on a file, you can alter its contents. For a directory, this means you can create files and subdirectories within that directory. It also means you can remove files from that directory even if you do not have write permission on the files.
--w--w--w-   1 johnd   unixdoc     8660 Feb 25 13:08 2.start
The ``w'' in the owner's set, the group set and the other users' set means that all classes of user can alter this file.

You cannot remove a file unless you have write permission on the directory it is stored in. If you try to remove a file from a directory for which you do not have write permission, you will see an error message like the following:

$ rm freds.file
rm: fred/freds.file not removed.
Permission denied

Execute
For a file, this means that if the file is a program, you can execute it. Execute permission on a directory means you can change to it.
---x--x--x   1 johnd   unixdoc      Feb 25 13:08 2.start
In all cases, a hyphen in any of the permissions fields indicates that the permission is not set.

More uncommonly, you may encounter other permissions in a long listing, for example ``s'' or ``t''. For details, see ls(C). 

To see the permissions on the current directory, use the l -d (directory) command, as follows:

   $ l -d
   drwxrwxrwx  21 johnd   techpubs    1552 Dec 07 15:40 .

Changing file permissions

To change the permissions on a file, use the chmod(C) (change mode) command, which has two formats, ``symbolic'' and ``absolute'', as follows:

chmod who operator permission filename
chmod mode filename

Using the first, symbolic, format, the who field is one or more of the following characters:

a
all users; change all three sets of permissions at once

u
user; change the user, or owner, permissions

g
group; change the group members' permissions

o
others; change the other users' permissions
The operator field is one of the following symbols:

+
add a new permission

-
remove a new permission

=
set permissions while clearing (removing) all other permissions
The following sample usages of chmod show a number of symbolic permissions being set:

$ chmod g+w memo
adds write permission for group members on the file memo.

$ chmod o-wx memo
removes write and execute permission for others (users other than the owner or those in the file's group).

$ chmod o= memo
clears (removes) all permissions for other (setting a NULL permission clears any existing value).

$ chmod u=rx memo
sets read and execute for user, clearing (removing) write permission (which is not specified in the ``='' command.)

$ chmod a+w memo
adds write permission to the existing permissions for all categories of user.
You can also change permissions using their absolute numeric values, by giving a three-digit octal number to specify the permissions. This method is harder to use but less verbose.

Using octal numbers to set permissions

 ----------------------------------------------------------------------
 Permissions   Octal number
 ----------------------------------------------------------------------
 ---           0
 --x           1
 -w-           2
 -wx           3
 r--           4
 r-x           5
 rw-           6
 rwx           7
Permission to execute a file is represented by a value of 1. Permission to write a file is represented by a value of 2. Permission to read a file is represented by a value of 4. These values are added together to produce the combinations in the table above.

Three octal numbers (numbers in the range 0 to 7) are used to represent the owner, group and other permissions respectively. Thus, by adding the permissions for a given category of user, you produce a digit; and by specifying three digits (one for each set of users) you can specify all the permissions on a file, as follows:

   $ l myfile
   -rw-r--r--   1 johnd techpubs    5061 Feb 10 15:01 myfile
   $ chmod 640 myfile 
   $ l myfile
   -rw-r-----   1 johnd techpubs    5061 Feb 10 15:01 myfile
myfile originally possessed permissions 644. The ``6'' gives read and write permissions (2 plus 4) to users in the specified group, while the ``4'' gives read permissions only. ``0'' gives no permissions at all. The effect of executing chmod 640 on this file was to deny all permissions to users of group ``other''.

Setting the default permissions for a new file

When new files are created, their initial permissions are determined by their file creation mask. The umask(C) command is executed whenever you log in, and it automatically sets the mask to restrict the permissions placed on any files that you create. You can change the permissions placed on new files by running umask again; the new permissions override the old ones.

To change the permissions applied to a newly created file, specify the permissions you want to have removed from the new file. In this way, specifying a file creation mask of o=rwx causes read, write and execute permission to be denied to other users.

   $ touch test 
   $ l test
   -rw-rw-r--   1 charles techpubs       0 Feb 22 09:29 test
   $ umask u=,g=w,o=rwx 
   $ touch test.2 
   $ l test.2 
   -rw-r-----   1 charles techpubs       0 Feb 22 09:30 test.2
The touch(C) command creates an empty file, in this case called test.

In the command lines above, the umask command specifies that write permission is to be removed from members of the file's group, and that read, write, and execute permissions are to be removed from other users. No change is made to the permissions available to the file's owner.


NOTE: Where the = operator is used in umask, it has the opposite effect to the = in chmod. With chmod, it sets any specified permissions, and unsets the rest, whereas with umask, it unsets the specified permissions while setting all the others.

Note that you cannot normally create an executable file using umask; you can only change a file's permissions to make it executable. For example, if your umask is umask u=,g=,o=rwx this gives your default file permissions of 660 (rw-rw----), not 770 (rwxrwx---), even though execute permissions for user and group have not been removed. The only exceptions to this rule are when creating a directory or compiling a program to create an executable binary (in which case the executable bits are set in accordance with your umask).

You can set umask using octal permissions. To set the umask, work out what permissions you want to give newly created files in octal, then subtract them from 777. (Remember, the permissions specified in your umask are removed from the file, not added.) Accordingly, umask 022 removes write permission from the group and other user classes: a file created with an initial mode of 777 becomes 755 and a file created with 666 becomes 644.

Giving a file to someone else

To give a file to someone else, change the ownership of the file with the chown(C) (change owner) command, as follows:

chown new_owner filename

The new_owner argument is the login name of the new owner.

For example, the following command line assigns ownership of 01.intro to the user charles:

   $ chown charles 01.intro
You must be the current owner of a file to change its ownership; that is, you cannot give the file to someone else unless it is yours to give. When you create a file, you automatically become its owner.

Depending on the permissions on a file, if you give away ownership you may give away your right to access the file afterwards.

Finding out your group

In order to find out the groups of which you are a member, use the id(C) command, as follows: 

   $ id
   uid=13052(johnd) gid=1014(techpubs)
The command displays your numeric user identification (UID) and your group identification (GID). Your login and group names are given in parentheses.

Changing your current group

Group control is carried out using the sg(C) (supplementary group) command. Type id (see ``Finding out your group'') or sg to obtain a list of the groups of which you are a member, as follows: 

   $ sg
   Current effective supplemental groups:
   1014(techpubs)
You can change your current group by using the sg -g option, as follows:
   $ sg -g techpubs
You must be recognized as a member of the new group before you can switch to it. Group memberships are listed in the file /etc/group; each group has a line in the file, followed by the names of those users who are authorized to work in it. After successfully changing group, you work within the new group for the remainder of the login session (or until you run sg -g again).

Changing the group of a file

To change the group of a file, use the chgrp(C) (change group) command, as follows:

chgrp new_group filename

For example, to change the group of a file called using_unix to techpubs, use the following command:

   $ chgrp techpubs using_unix
Files and users on the system are identified as members of a group by their group name. Groups, together with group permissions, allow people who need to use the same files to share those files without sharing them with all users. When you create a file, it is automatically given the same group as your own. You must be the owner of a file to change its group.

Printing a file

When you issue a print command, a copy of the file to be printed is spooled. It then waits in the print queue, along with other print jobs, until its turn comes to be printed. Because the system spools print jobs, you can go straight on to another task.

To print a file use the lp(C) (line printer) command. For example:

   $ lp myfile
   request id is laserwriter-635 (1 file)
This command sends myfile to the print queue. The ``request id'' line means that the file will be printed on the printer named ``laserwriter'', and is request number 635.

To print several files, add them to the command line, as follows:

   $ lp file1 file2 file3
This command line prints file1, followed by file2, followed by file3.

Note that it is a bad idea to print executable programs, or other files containing binary data; in general you should only print files containing text, or containing some form of data intended for printing (such as PostScript® files).

By default, files are printed in portrait orientation on the paper: to print a file in landscape orientation (that is, sideways, so that long lines fit on the page), use the following command:

   $ lp -ol file1
To print a PostScript file on a PostScript printer, you should specify that the file contains PostScript, by using the following command:
   $ lp -og
See lp(C) for more information about how to send files to the printer.

If you need to add page numbers to a long file, use the pr(C) command, which prints files to its standard output, separated into pages with a header containing the page number and date and time of printing. You can then pipe the paginated output to lp.

For example, to print /etc/profile in this way, use the following command line:

   $ pr /etc/profile | lp

Printing several copies of a file

To print several copies of a file, use the lp -n option, as follows:

   $ lp -n 3 file1
The argument to -n is the number of copies you want, in this case, three. The default number is 1.

Selecting a printer

If you know that several printers are connected to your system, and you want to send a file to a printer that is not busy, you need to know the destination printer's name. You can get a list of the printers available to you by using the lpstat(C) (line printer status) command, as follows: 

   $ lpstat -s
To select one of the available printers, use the lp -d option, as follows:
   $ lp -dlaserwriter2 file1 file2 file3
This command line sends the specified files to the named printer.

You can assign a default printer for lp to use, by setting the LPDEST environment variable. Environment variables are explained in depth in ``Setting shell variables''. Add a line setting the value of LPDEST to the name of your default printer to the appropriate login script. (Login scripts are described in ``What happens when you log in''.)

For example, if you are using the Korn shell and the printer you want to use by default is called postscript-2, you can add the following line to your .profile file:

   LPDEST=postscript-2; export LPDEST
postscript-2 will then become your default printer next time you log in.

Displaying a list of current print jobs

To display the list of current print jobs, use the following command line:

   $ lpstat -o
lpstat reports on printer status.

Canceling a print request

To cancel a print request, use the cancel(C) command. You need to know the print request number that was assigned when the request was first spooled. This can be found using lpstat -o. For example:

   $ cancel laserwriter-635
You can only cancel your own print requests.

Getting help on the command line

Extensive online help is provided by the man(C) system. man is short for ``manual'', and is a tool for retrieving the text of the reference manuals.

In this book, you will sometimes see reference keywords followed by a letter in brackets; for example, ls(C) or regexp(M). The letter in brackets indicates the section of the reference manual in which the keyword is discussed. If you are working at the shell prompt, you can read the reference entry for the keyword by entering the following command:

man [section] keyword

The section field is optional and is used to select a particular section when a keyword is documented in more than one section. For example, type man C kill to read the C section entry on the keyword kill (which is documented as a command in section C and a callable function in section S).

The reference manual entries are technical descriptions; they are not tutorials and make no concessions to the inexperienced user.

Getting help when you are uncertain of the topic

If you know the keyword but do not want to read all the reference text, you can use the whatis(C) command to list the description of the item. For example, to read the description of man, type the following:

   $ whatis man
   man(C)          - prints reference pages in this guide
If you are not sure of the keyword to use for a topic, you can use the apropos(C) command (which is the same as man -k). Each entry in the reference manual has a description associated with it; apropos searches the descriptions for the word you give as a subject. For example, to find reference entries concerned with searching, type apropos search. The following entries are among those displayed:
   egrep(C)                - Search a file for one or more patterns
   fgrep(C)                - Search a file for a fixed string
   grep (C)                - Search a file for a pattern
You can then use man C grep, for example, to display the manual page on the grep command.