Friday, December 26, 2008

Problems galore, Solutions aplenty

A few years ago, one of my friends asked me for advice. He was frustrated, mostly out of his job. I wanted to help, but he was at the other side of the globe! I couldn't give him a ready solution. But instead, I sent him my recepie of solving a problem. Here it is.

1. Identify the problem
This is the first step. Unless we identify the exact problem, with all its details, we won't be able to prepare a solution for it. If you could prepare a problem statement, not more than a few lines, that would help much to make things clear.

2. Make a list of all possible solutions
When you are aware what the problem is or what is bothering you or what is it that you are not satisfied with, think of all the possible ways in which the situation could be improved. Even if a solution looks simple, or complex, far fetched, or lengthy, do add it to your list. Include only practical solutions.

3. Choose the best solution from the list
When you know the all possible ways, choose the best one. Depends on what is best for you in that situation. Choosing the best one would require comparisons and judgement on the criteria that you have.

4. Implement the best solution that we now know
Go ahead, its time for action.

When I have a problem, I tend to think in this way. And it works for me. And who doesn't have problems in this world? Sometimes I think that we are here to solve problems. We are all problem-solvers.

Sunday, December 21, 2008

screen 'em

Some of the tasks that I do involve running commands that take way long to finish - some take a few hours, others take a couple of days. Obviously, you can't be staring at the terminal till the task is done. And if you close the terminal, the command dies away. So how to run these commands? Simply running them in background won't help, as they would be terminated when you log out.

One idea is to run them in background, and using nohup.

$ nohup my_cmd &
$ exit

This would start the command in the background in such a way that it won't be stopped even if you log out. But you won't see the output at your terminal. nohup would redirect it to a file named nohup.out

Once I start the command using nohup, there is no way I can interact with it. And I can't see the output at the terminal. Sure, there is a way if I really want to. Keep watching: $ tail -f nohup.out

A better alternative in these scenarios is to use the GNU Screen utility. It offers many useful features.

* I can start the command and leave it alone. Later, I can see how it is progressing whenever I want to.

* I can start the command from one computer, and then see how it is progressing from a different computer. So I can start the task at office, then go home, and when I find time, check how it is going.

* More than one users can share a screen. So if my colleague wants to check, he can also connect to the same screen and see the progress.

* Multiple terminal sessions can be created in a screen session. So I can keep running the command in one of them, and do something else in the other.

* Screen is very helpful when the network connection is unreliable. I would still have the task running even if connection breaks.

Enough. Tel me how do I start.

Check if a screen session is already running
$ screen -ls

Attach to a not detached session
$ screen -x

Start a new screen session
$ screen

Create a new terminal session
CTRL+a c

Toggle between two terminal sessions
CTRL+a CTRL+a

Go to the nth terminal session
CTRL+a n

Send the command character CTRL+a to a window
CTRL+a a

Detach from a screen
CTRL+a d

Terminate the screen
$ exit

man screen for more information

Saturday, December 20, 2008

bad command or file name

Really? Is my command so bad? Then what would a good command look like?

Long gone are the days when I last saw the (in)famous error message. Most of us would have forgotten it by now. One of the blogs that I was reading today mentioned about it. And in a rhetoric manner.

Why would you want to tell someone that something they entered is bad? Or something bad has happened? Isn't that really foolish? Didn't Mom tell them: "If you can't say anything nice, don't say anything at all." At least, don't say a bad word.

A major reason why software is not easy to learn is that it doesn't give positive feedback. We learn better from positive feedback, rather than from negative feedback. Would you prefer a hired ski instructor who yells at you? Or a restaurant host who loudly announces to other patrons that your credit card was rejected? Certainly not. Then why the should software tell me that I failed?

In my opinion, to be given negative feedback by software - any software - is an insult to human. Yes, I've read the three laws of robotics, and can recite them for you even during sleep.

Forget robotics and the mean machines, but for me, to be told by software that you have failed is degrading. And the same should be for everyone. There is nothing so important inside that dumb box that you can justify degrading a human user. If you want to protect you files, do it. But do so without bothering me. It's all right to protect the computer but not at the cost of bothering the user.

If I mistype a command, with bash, csh, sh, tcsh, zsh - they all tell me:
command not found

Well, that's better - it tells me without insulting me that my input could not be processed. And it even hints that what I entered was treated as a command, without misleading me to file names.

An error message is the program reporting on its failure to do the job, and it is interrupting the user to do this. So it must be polite, illuminating, and helpful.

Have you never heard - The customer is always right

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)

Monday, July 28, 2008

Changing size of a file

How do you change the size of a file in Linux? If you are not concerned about the contents of the file, use the dd command.

dd if=/dev/zero of=/foo/bar bs=1024 count=1
This would change the size of a file to 1 KB. inode number of the file would be preserved.

So if you want to change the size to 2 MB
dd if=/dev/zero of=/foo/bar bs=1024 count=2048

Note that 2 MB is 2048 KB, and not 2000 KB.

Sunday, July 27, 2008

Epigram on a Software Laird

Bless Linus Torvalds, O Engineer,
With grateful, lifted eyes,
Who taught that not the windows alone,
But doors too shall open;
For had He said "the doors alone
From source I will deliver,"
Alas, alas! O Engineer,
Then hadst thou lain for ever.

Original work: Epigram on a Country Laird by Robert Burns

Tuesday, July 22, 2008

Sorting alphanumeric strings in Perl

One of my friends had trouble sorting alphanumeric strings in Perl. Strings he had were similar to:
child1
child3
child11
child51
child5
child24

He wanted to sort them based on the numbers that follow after the alphabtic part. So, the order he wanted:

child1
child3
child5
child11
child24
child51

I extracted the numeric part from the alphanumeric string and used it for comparisons
@alphanumeric_sorted = sort { substr($a, 5) <=> substr($b, 5)} @alphanumeric;

Monday, July 21, 2008

Apache Lyrics

Sometime last year I wrote parody of a famous song. Some of the lines of this song are in Spanish, and you need to know a bit of Spanish to understand them.

"Ports Won't Die"
(feat. Office Assistant)

Macros up in here tonight
No fighting, no fighting
We got the fixes up in here
No fighting, no fighting

Apache, Apache

I never really knew that it could serve so fast
It makes a man wants to HTTP
Como se llama (si), bonita (si), mi OS (si, Apache Apache), su OS
Apache, Apache

Oh baby when you talk like that
You make IIS go mad
So be wise and keep on
Reading the signs of my Ballmer

And I'm on tonight
You know my ports won't die
And I'm starting to feel it's right
All the attraction, the tension
Don't you see baby, this is perfection

Hey Girl, I can see your traffic moving
And it's driving me crazy
And I didn't have the slightest idea
Until I saw you serving

And when you take up a bad request
Nobody cannot ignore the way you check it out, girl
And architecture so perfected - the way you right and left it
So you can keep on excelling

I never really knew that it could serve so fast
It makes a man want to HTTP
Como se llama (si), bonita (si), mi OS (si, Apache Apache), su OS
Apache, Apache

Oh baby when you talk like that
You make IIS go mad
So be wise and keep on
Reading the signs of my Ballmer

And I'm on tonight
You know my ports won't die
And I am starting to feel you boy
Come on lets go, real slow
Don't you see baby asi es perfecto

Oh I know I am on tonight my ports won't die
And I am starting to feel it's right
All the attraction, the tension
Don't you see baby, this is perfection
Apache, Apache

Oh boy, I can see your traffic moving
Half filtered, half cached
I don't, don't really know what I'm doing
But you seem to have a plan
My will and self restraint
Have come to fail now, fail now
See, I am doing what I can, but I can't so you know
That's a bit too hard to explain

Roca en el Internet toda la noche
Roca en el Internet toda el día

Roca en el Internet toda la noche
Roca en el Internet toda el día

I never really knew that it could serve so fast
It makes a man want to HTTP
Como se llama (si), bonita (si), mi OS (si, Apache Apache), su OS
Apache, Apache

Oh baby when you talk like that
You know you got me hypnotized
So be wise and keep on
Reading the signs of my Ballmer

Senorita, feel the conga, let me see you serve like you come from Redmond

Mira en Maryland se baila así, say it!
Mira en Maryland se baila así

Yeah
It's so popular, macro shaft's jealousy a refugee like me back with the Fugees from a buggy territory
I go back like when 'pac carried crates for Humpty Humpty
I need a whole club dizzy
Why the Court wanna break us?
Explorers and Windows
I ain't guilty, it's no monopoly transaction
No more do we snatch ropes
Refugees run the seas 'cause we own our own browser

I'm on tonight, my ports won't die
And I'm starting to feel you boy
Come on let's go, real slow
Baby, like this is perfecto

Oh, you know I am on tonight and my ports won't die
And I am starting to feel it's right
The attraction, the tension
Baby, like this is perfection

No fighting
No fighting