Monday, November 17, 2008

How to count occurences of a tag in an xml file

How can you find out total occurrences of a particular tag in an XML file? Say, I want to find out how many property tags are present in an XML file.

grep is not sufficient for this task. Here is a perl script that does the job.

#!/usr/bin/perl
# count_xml_tags.pl
my $xml_tag = shift;
my $filename = shift;

my $count     = 0;
open (X_FILE, '<', $filename) or die "Failed to read file $filename : $!";
{
    local $/;
    while (<X_FILE>) {
        while (m#<$xml_tag>(.*?)</$xml_tag>#gs) {
            $count++;
        }
    }
}
close (X_FILE);
print "$count $xml_tag tag(s) found.\n";

Run this script as:
% perl count_xml_tags.pl some_tag filename.xml

How to remove duplicate lines from a file

Our on-line publishing system has a text file that contains certain entries, one per line. Some entries were duplicate, and we wanted to remove them.

This can be done using sort and uniq commands.
sort /foo/bar | uniq > /new/bar

But we wanted to retain the order of lines, and so didn't want to sort the file. I found a solution using awk.
awk '!x[$0]++' /foo/bar > /new/bar

And how do I check if the file contains duplicate lines or not? The -d option of uniq command is helpful in this case.
sort /foo/bar | uniq -d

Saturday, August 2, 2008

How to convert Unix time to human readable format

Unix-like operating systems maintain time as the number of seconds elapsed since midnight UTC of January 1, 1970. For example, 1217646573 represents Sat Aug 2 08:39:33 2008.

How do you convert time mentioned in the Unix time format to human readable format? Perl is handy in this situation.
% perl -e 'print scalar localtime(1217646573), "\n";'

I used to work with a network monitoring software that produced logs containing time stamps in Unix time. Analyzing the logs would be difficult unless the time stamps were replaced by a human readable format.

The logs were as shown below.
STATUS [1217650382] Event: Description

Here's a perl one-liner that does the job.
% perl -pi -e 's#(?<=\[)(\d+)(?=])#scalar localtime($2)#e' /foo/bar

Friday, August 1, 2008

My Geek Code

-----BEGIN GEEK CODE BLOCK-----
Version 1.0
GO d-@ s: a- C++$ UBL++>$ P+++>$ L+++>$
E- W++ N+ o K w-- !O- !M- V-
@PS+ @PE++ Y+ !PGP
t 5 X R+++ tv b+> DI++ D G
e++ h--- r+++ y+++
------END GEEK CODE BLOCK------

Thursday, July 31, 2008

How to find out if a given year is a leap year or not?

Well, for that, how do you define a leap year?
Leap year is every year the number of which is a multiple of four, except those devisible by 100 and not by 400.

Here are the steps to determine if a given year is a leap year or not:
if year modulo 400 is 0 then leap
    else if year modulo 100 is 0 then no_leap
    else if year modulo 4 is 0 then leap
    else no_leap

Here is one Perl implementation.

#!/usr/bin/perl
use strict;
use warnings;
###### is_leap_year ######
# Purpose : To determine whether a given year is a leap year or not
# Arguements : One
# First (Number): Year to be checked
# Returns : Boolean value indicating whether given year is a leap year or not
# true = given year is a leap year
# false = given year is not a leap year
############################
sub is_leap_year ($)
{
       my $year = shift;
       return unless ($year =~ m/^\d+$/);

       if (($year % 400) == 0) {
       # This year is completely divisible by 400
               return 1;     # This is a leap year
       }
       elsif (($year % 100) == 0) {
       # This year is completely divisible by 100
               return 0;     # This is not a leap year
       }
       elsif (($year % 4) == 0) {
       # This year is completely divisible by 4
               return 1;     # This is a leap year
       }
       return 0;     # This is not a leap year
}

my $this_year = 2040;
if (is_leap_year ($this_year)) {
       print $this_year, " is a leap year\n";
}
else {
       print $this_year, " is a not a leap year\n";
}

Wednesday, July 30, 2008

Count number of occurrences of a word

How can you count number of occurrences of a word in a file? Say, I want to count how many times ring is present in a file.

How about using the -c option of grep?
% grep -c ring /foo/bar

And what if the file contains words such as string and rings? That would break our count of ring.
Using the -w option of grep solves this problem
% grep -cw ring /foo/bar

What if the word ring is present more than once in a line? grep would produce incorrect count in this case, since it counts the number of lines in which the pattern is found. grep is not sufficient for this job. We need something that can count multiple occurrences of a word in a line.

To get the exact count:
#!/usr/bin/perl
# search_word.pl
my $search_this = shift or exit 1;
my $count = 0;
while (<>) {
    while (m/\b$search_this\b/g) {
        $count++;
    }
}
if ($count == 0) {
    print $ARGV . "does not contain " . $search_this . "\n";
}
else {
    print $ARGV . "contains " . $search_this . " " . $count . (($count == 1) ? " time\n" : " times\n");
}


Run this perl script as:
% perl search_word.pl ring filename

And what if I don't want to use Perl? Well, then this should work for you:
% grep -w -o ring /foo/bar | wc -l

Tuesday, July 29, 2008

Why not to use awk when sed can do the job?

  • Using awk instead of sed has the price of performance and size
  • compared to sed and ed, awk takes a substantially longer time to load, and does its job at a considerably slower pace
  • The real distinguishing point between sed and awk as a text processor is that awk is able to work with a persistent context, whereas capabilities of sed in this area are limited to non-existent. If you - for instance - would have to sum one field to a total you would do it with awk (it would be possible to do it with sed, but would be a nightmare - poorly suited tool for the job)