Cover V14, i04

Article

apr2005.tar

See Your Files and Descriptions with "List"

Jim Pinson

The Unix/Linux world loves short file names, especially for commands. In a world dominated by "ls", "cp", and "mv", longer filenames such as "md5sum" seem almost extravagant. The short file names certainly save us a lot of effort when we type commands, especially those long pipe commands we seem to favor.

Of course, there is a price to pay. Short names for seldom-used files, especially those we didn't write, convey little about the files' functions. We can create long file names that clearly spell out the purpose of a file, but the longer and more descriptive our file name, the harder it is to type, and the long name messes up our "ls" output. Some file names simply can't be renamed to explain their function, because they are used by other programs, such as those in the /etc directory.

Years ago, I saw a solution to this type of problem in a COMMAND.COM replacement called "4-DOS". This program allowed you to give descriptions to files, and those descriptions could be displayed when you listed your directory contents. This functionality can be easily added to current Unix, Linux, and Cygwin systems using a Perl script I wrote called "list". I have found this program very useful over the years, particularly for keeping track of all my old scripts.

The "list" program works very much like "ls". It lists your files, but instead of displaying the normal file attributes, it displays a file description. It also has commmand-line switches that allow you to add descriptions and copy or move files along with their descriptions.

"List" is quite simple to use. Just type "list" to see the files and descriptions in the current directory, or type "list" followed by the filenames and directories you wish to view. You can freely mix directories and filenames on the command line. ("List" can be obtained from the Sys Admin Web site: http://www.sysadminmag.com/code/).

To add a description to the file /mydata/serverlist.txt, you would type:

list -add /mydata/servers.txt "This is a list of all my servers"
If you were already in the /mydata directory, you could type:

list -add servers.txt "This is a list of all my servers"
To see the description you just added, along with all the other .txt files in the directory, you could type:

list /mydata/*.txt
Typing that command would produce output that looks like this:

Directory /mydata:

employee.txt
supervisors.txt
servers.txt    This is a list of all my servers
tools.txt
All switches can be shortened to a single letter, or given in POSIX form. For example, the -add switch can be entered as -add, --add, or -a. However, for purposes of clarity in this article, I will use the full switch name for all the examples.

Suppose you want to move the file to a new location. You could type:

list -move /mydata/servers.txt /backups
This command would move the file and the description to the new directory. You could verify that the file and its description were moved by typing:

list /backups/servers.txt
The "list" program is quite flexible and can work on multiple files and/or directories at one time. For example, all the following commands are valid:

list /bin /usr/bin  /mydata/servers.txt
list -copy /mydata/*.txt /backup
list -copy /mydata/employee.txt /data/emp.txt /backupdir
The Description File

But, where is the description of the files kept? I debated using databases such as MySQL or SQLite to store the description, but decided to stick with the idea of a description file, located in each directory. There is a certain elegance to this approach. It means any normal tool you use to back up a whole directory will back up the descriptions as well.

If you copy a directory to a CD, or move a directory tree, the description goes along for the ride. "List" is not dependent on another tool, such as a database. An advantage of the file being plain text is that even users without "list" can view the file contents.

By default, the descriptions file is called "DESCRIPTIONS". The format is quite simple: each line of the file contains a filename, the separation character (a "~" ), and the description. For example:

DESCRIPTIONS~This is my descriptions file
servers.txt~This is a list of all my servers
The actual name of the file used to contain the descriptions, as well as the separator character, can be changed in the first few lines of the "list" program in a section I will refer to as the "config" section. (The complete list of config options will be discussed later in this article.)

You can of course edit the DESCRIPTIONS file directly, or use the -add switch discussed above to create entries. I highly recommend using the -add switch to add descriptions, particularly when dealing with large directories with many descriptions.

Another way to get descriptions into the file is to have "list" search files for embedded descriptions by typing:

list -scan /mydata
The -scan switch will search all text files in the specified directory for a special markup that contains the file description. By default, the begin/end blocks of the markup are ::bdes:: and ::edes::.

The description for servers.txt could be added to server.txt as:

::bdes::This is a list of all my servers::edes::
"List" will ignore any text before and after the begin/end blocks, so you can protect the description with whatever "comment" delimiters your programming language uses.

The -scan option only processes plain text files in the directory specified; binary files are not currently supported.

The text recognized as begin/end blocks by "list" can be customized in the config section of "list".

If everyone in your work group agrees on a uniform system to add internal descriptions to scripts and datafiles, keeping your DESCRIPTIONS file up to date will be much easier.

Since I practice what I preach, "list" has a description of itself in the source. Whenever "list" alters a DESCRIPTION file, it forces the first line to be an embedded description.

Formatting Long Descriptions

The DESCRIPTIONS file uses one line per filename/description. Even if a long description is entered, the "list" program will not truncate it. The "list" program will wrap long descriptions to fit your current term window when it prints them, as displayed below:

list /mydata/*.txt

employee.txt
supervisors.txt
servers.txt             This is a list of all my servers.
long.txt                This is a text file that has a very very
                        very very very very very very very very
                        very, let me say this again, a very very
                        long description.
sales.txt               A list of our sales team.
As "list" prints out files and descriptions, it uses the first 25 characters of the output for the filename and the remainder of the display for the description. Word wraps occur on white space of the description, and if there is no white space, the line is broken mid-word as needed. This, of course, requires that "list" know the length of your current term window.

Many shells have an environment setting, "COLUMNS", which contains your current term length. Unfortunately, some shells don't export this variable so it is not available to "list". I recommend you export your COLUMNS environment in your shell startup script so "list" can use it.

If "list" doesn't find COLUMNS in the environment, it will attempt to invoke "tput cols" and "stty size" to find the setting. If these approaches do not work, "list" will assume a default term width of 80 characters.

The Details

To install "list", just copy the "list" program to someplace in your PATH and change the first line (the #! "SHE BANG") to indicate the path to your Perl program.

"List" is written for Perl 5 and only uses standard Perl Library modules, so it should work with most Unix and Linux systems. I even use it with Cygwin on Windows systems.

If your shell doesn't export COLUMNS, please do so in your startup script and add any aliases you might like to reduce your typing. For example, the .bashrc file in my home directory has these entries:

export COLUMNS
alias add="list -add"
alias move="list -move"
alias copy="list -copy"
Command-Line Switches

The following switches are recognized by "list":

-add -- Adds a description to a single file or directory. Examples:

list -add myfile.txt "this is a description of my file"
list -add mybin      "this is my private bin directory"
-copy -- Copies file(s) and descriptions. Examples:

list -copy file1 file2           (one file to another file)
list -copy file1 file2 outputdir (multiple files to a directory)
-directory -- Tells "list" you want to see the description of a directory, not the contents of the directory. This works like the -d switch of the ls command. Example: Suppose you add a description to the /bin directory using the command:

list -add /bin "This is the main bin directory"
That command will add the description of /bin to the description file /DESCRIPTIONS. However, if you type list /bin, you will not see the description of "/bin" you just added. Instead, you will see the files and subdirectories of /bin. To see the description of the directory /bin and nothing else, type list -directory /bin.

-move -- Moves file[s] and descriptions. Examples:

list -move file1 file2                   (one file to another file)
list -move fileone file2 file3 outputdir (multiple files to a directory)
-scan -- Scans all the text files in a directory for embedded descriptions. Multiple directories may be specified. Examples:

list -scan mydirectory
list -scan mydirectory1 mydirectory2
-purge -- Checks a directory or directories to see if all the descriptions have files to go with them. If the file doesn't exist, the "orphan" description is removed. Examples:

list -purge mydirectory
list -purge mydirectory mydirectory2
-help -- Prints program usage help.

Preserving Your Descriptions File

"List" tries to avoid damage to your DESCRIPTIONS file, since it can represent much effort on your part to produce. Whenever "list" updates DESCRIPTIONS, it makes a backup of the original, called "DESCRIPTIONS.bak". Whenever you move or copy files to a new directory, "list" updates the destination directory's DESCRIPTIONS file with the new description, but it does not by default remove the descriptions from the source DESCRIPTIONS file. Of course, over time you could end up with a lot of old descriptions. This is not really a problem, because you won't see these old descriptions during your "list" operations. However, "list" does provide two ways to remove old descriptions. The first is to purge the directory using the -purge switch described above. The second method is to set the "autopurge" feature in the config section of the "list" source file.

When autopurge is active, "list" will automatically purge the DESCRIPTIONS file of orphaned descriptions whenever it updates the DESCRIPTIONS file (e.g., during -add, -move, or -copy operations.) While autopurge may sound attractive, I generally suggest it not be activated. Imagine, for example, that a user forgets to use list -move and instead uses a system command like mv to move files. That would mean the files would exist in another directory without their descriptions. The descriptions would remain in the old directory as orphans but would disappear from the DESCRIPTIONS file the next time you added a description. I think it is safer to manually purge the descriptions at times of your choosing.

As mentioned earlier, it is a wise practice to embed each description in its respective file so that the descriptions can be recovered using the -scan switch, no matter where the files end up.

I've mentioned the config section several times now, so let's take a look at it:

###################### config section.######################
#change as needed

my $descriptionFile="DESCRIPTONS";   #The name used to store descriptions
my $desDelimiter="~";   #delimiter to separate filename from description.

my $nameLength=25;   #The filename will be formatted to this length.
                     #The actual description will use
                     #the remainder of the screen length.

my $delimiterStart= "::bdes::";  #beginning of in-file description
my $delimiterEnd=   "::bdes::";  #end of in-file description. 

my $autopurge=0;   #change to "my $autopurge=1;" to activate autopurge

###########################################################
Conclusion

I've found "list" to be a great addition to my tool box, I hope it works for you. If you build a good description file for various directories on any of the major Unix/Linux distributions, please feel free to send it to me, and I will post it along with future updates on: http://www.jc-research.com/downloads.

Jim Pinson is a Linux Administrator, Systems Programmer, Oracle OCP DBA, and technical writer. He provides consulting services through his company, J&C Research, Inc. of Havana, Florida (near Tallahassee). He can be contacted at: jim_pinson@jc-research.com.