|
|||
|
Defining Interface Specifications When I create a user interface, I like to make sure it is flexible and expandable. Although I could just hard-code the exact functionality I need at the moment, it is better in the long term to define the interface using a configuration file (as much as possible). There are really an infinite number of options for such a configuration file. I like to place one item on each line, where each item can have any number of name/value pairs. These are separated by the pipe character (|). Here is an example: |variable=item1|type=integer|default=0|desc=Sample Item 1 |variable=item2|type=string|default=|desc=Sample Item 2 As you can see, each line represents one item. Each line has any number of name/value pairs in the form |name=value. Reading in bash When reading this type of file in a shell script, it is difficult to store it all in memory like we will do in Perl. This is not usually a problem, however, because a typical program just loops through one entry at a time. In this bash example, I define the function get_value that will return the value for a named parameter on the current line. FILE='/usr/local/etc/system.conf' get_value() { echo "$1" | sed "s/.*|$2=\([^|]*\).*/\1/" } cat "$FILE" | while read line ; do echo "Variable: $(get_value "$line" variable)" echo " Type: $(get_value "$line" type)" echo " Default: $(get_value "$line" default)" echo " Description: $(get_value "$line" desc)" done If executed, this example simply shows all of the entries and the specified value for each named parameter. Remember that everything within the while loop is executed in a subshell, so variables set in there will not propagate to any code outside of the loop. Reading in Perl Since memory is rarely at a premium these days, and since this type of configuration file is almost always very small, I find it much easier to just read the whole thing into memory and work with it there. This is fairly simple to do in Perl, of course: sub read_pipe_file ($) { my ($file) = @_; my (@ret, $entry); open(FILE, $file) or return undef; while (my $line = <FILE>) { chomp($line); next if ($line =~ /^\s*$/); # skip blank lines next if ($line =~ /^\s*#/); # skip comments # Now retrieve name/values for current line $entry = (); while ($line =~ s/^\|([^=|]+)=([^|]*)//) { $entry->{$1} = $2; } push @ret, $entry; } close(FILE); return (@ret); } This function reads a file and returns an array of references. Each of these references represents one line (or entry) in the configuration file. These are references to hashes that contain the various name/value pairs that belong to the entry. Note that the order of the lines is preserved, but not the order of the name/value pairs. Using the function is easy. Here is a small snippet of code that you can use to test this function (it just displays all of the name/value pairs from the file): #!/usr/bin/perl -w use strict; foreach my $entry (read_pipe_file '/tmp/test.conf') { foreach (keys %{$entry}) { print "$_: $entry->{$_}\n"; } } |