Sunday, December 14, 2008

Fun with Linux (Too technical, you can skip this)

The best thing about linux is that you always discover new things while working with it. For example, I recently discovered that you can open GUI windows from the shell prompt by using a command called zenity. This was very interesting as it widens the gap between console programming (which every student is taught) and GUI programming (which a developer has to know to make anything useful) considerably.

Now I could do stuff like display my own notifications:


This was done by a simple command like this:
zenity --info --text "Hello World"

but of course, a window showing "Hello World" is not very useful.

Ever wonder why all computer language books give a "hello world" program as their first example? I mean who wants a program that says hello world and does nothing else? See, this is not the way to attract potential new programmers to the world of coding...


So, anyway, I decided to make a practical app to see what I can do with this... but what to do? I got the idea after piping the output of ps to a text box:



Hmm, I could make my own process manager, which would be a most rudimentary app to hunt and kill processes running. The element I would be using is a list box (zenity --list). This unfortunately does not allow output to be piped in. So I had to write a perl script which converts stdin to command line option for a zenity list:


#!/usr/bin/perl -w
use strict;
use Data::Dumper;

#my $seperator="\t"; #TODO: pass this as command line option.
my @columns;

my $line = <stdin>; #Input comes from standard input. Caller should redirect.
chomp $line;
$line =~ s/^\s+//;
@columns = split (/\s+/, $line);

my $columnCount = $#columns;

foreach my $column (@columns) {
print qq{ --column $column };
}

while ($line = <stdin>) {
chomp $line;
print qq{FALSE $line };
}
The alert reader will notice that this script will convert any tabular input into command line option for a zenity list - maximum reusability!

All that remains is to pipe the out of ps and take the output of zenity as parameter for kill:


#!/bin/bash
listWindow() {
psParams=pid,ppid,comm,time,pcpu,stat #Edit this to modify columns
zenity --list --text="Select the processes to kill:" --width 640 --height 480 --checklist --separator=" " --column Select \
$(ps -u $USER -o $psParams\
| tee >(zenity --progress --pulsate --auto-close)|perl /home/prasanna/bin/mkzenitylist.pl)
}

pid=$(listWindow)
while [[ -n "${pid}" ]]; do
zenity --question --text "Really kill processes with pids: $pid?" && (kill $pid ||
for i in $pid; do
if ps p $i >/dev/null 2>/dev/null; then
zenity --question --text "$i did not die: force kill?" && kill -9 $i
fi
done)
pid=$(listWindow)
done
Voila! One task manager ready! On launching, the first screen looks something like this:
Let's say I select 'gedit' and click ok:
Clicking ok will kill the process, and take me back to first screen. Clicking cancel would take me back to the original screen without killing anything.

This obviously is not the best process manager you can think of; It's not even real time (I certainly don't use this - I use the default task manager that comes with the OS :) )
But, it is nevertheless, usable. And that was the point of this exercise: to make a GUI app that is simple and usable in a real world scenario.

No comments: