Tip: prevent “die” from writing the script name and line number

Normally when you use “die” with a message in perl, the result looks like this:

Can't open registry key Software\MyCompany\MyKey at MyScript.pl line 112

So, next time, just add ‘\n’ to the die message, and you’ll get

Can't open registry key Software\MyCompany\MyKey

Strange… but works!

Advertisements

Getting a JIRA issue’s priority using Perl

JIRA::Client is a great and useful module. Too bad some of its usage is so cryptic!

For example – suppose you want to print an issue’s priority field. if you simply use $issue->{priority} you will get the priority id, not the display name which everyone is used too.
So, how to get the display name? documentation is very vague on this subject. Google doesn’t help much here.

After a lot of trial & error I figured out a way.

Continue reading

Handling CSV files in perl

I was writing a script to modify .CSV files (ClearQuest export results), and it was incredibly complex due to having multi-line fields (“embedded newlines” in CSV jargon).

After some struggling, I came across a built-in module (at least in ActivePerl 5.12.3) called Text::CSV_XS which basically does everything I wanted. I was able to cut my script length by about 60% and get better performance and (seemingly) bug-free results.
This module is easy to use and has good documentation.

Lesson learnt: re-inventing the wheel is not always the best course of action…

 

Propagating Perl exit code

I have a Perl script which calls another Perl script. The ‘child’ script returns the exit code of a system command and the ‘parent’ script should check it and act accordingly.

parent.pl:

my $result = system("perl child.pl");
print STDERR "Child script failed!\n" if ($result != 0);

child.pl:

my $exit_value = system($command);
exit($exit_value);

However, even if the system command fails, the parent script always receives “success”.

I finally discovered that Perl only considers the lower 8 bits of the exit() argument. Hence exit codes like 256 or 512 (which are quite common in operating system commands) are interpreted as 0.

I have two solutions for this problem:

1. If you need the exact exit code, Shift-right the exit code of the system command, then return the result.

my $result = system($command);
my $exit_value = $result >> 8;
exit($exit_value);

2. if you just need to know if the child command was successful, you can use “die” instead of “exit” in case the system command fails.

my $result = system($command);
die if ($result != 0);

Redirecting STDOUT and STDERR to file in perl

A small issue, but very common. There are several methods to do it, here is my favorite, which lets you print to the ‘original’ streams:

my $log_file = "/path/to/log/file.log";
redirect_streams();
print "Hello log file!\n";
print OLDOUT "Hello console!\n";
restore_streams();
exit(0);
##############################################################
sub restore_streams
{
  close(STDOUT) || die "Can't close STDOUT: $!";
  close(STDERR) || die "Can't close STDERR: $!";
  open(STDERR, ">&OLDERR") || die "Can't restore stderr: $!";
  open(STDOUT, ">&OLDOUT") || die "Can't restore stdout: $!";
}
##############################################################
sub redirect_streams
{
  open OLDOUT,">&STDOUT" || die "Can't duplicate STDOUT: $!";
  open OLDERR,">&STDERR" || die "Can't duplicate STDERR: $!";
  open(STDOUT,">> $log_file");
  open(STDERR,">&STDOUT");
}

 

Updating Windows Environment Variables

A well-known fact is that, in order to permanently set/modify an environment variable, one should update the Registry under:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment (System Variable)

or

HKEY_CURRENT_USER\Environment (User Variable)

However, this update does not go into effect until logging off and back on.

The solution is to broadcast a Windows Message to all running applications. The following C++ code provides the details:

SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM) "Environment", SMTO_ABORTIFHUNG, 5000, &dwReturnValue);

In Perl, you can simply use the AdminMisc module to set environment variables. If you prefer to stick to standard modules (perl 5.8.9 and later), you can use the following code fragment:

use Win32::API;

	use constant HWND_BROADCAST => -1;
	use constant WM_SETTINGCHANGE => 0x1a;

	my $SendMessage = new Win32::API("user32", "SendMessage",
'NNNP', 'N')
    	or die "Couldn't create SendMessage: $!\n";
	my $RetVal =
$SendMessage->Call(HWND_BROADCAST,WM_SETTINGCHANGE,0,'Environment');

Another alternative is to build the aforementioned C++ code into a small exe file, and use this utility anywhere you like.

In any case, note that the environment variable change will not be visible to the calling process. You’ll need to set it internally (for example, using $ENV in Perl) if you need it to be available inside the script.

References: Microsoft KB Article 104011, PerlMonks thread