1Nf3 has asked for the wisdom of the Perl Monks concerning the following question:

Hello, monks. I have a problem with gathering some information from Win registry.

My code works well when it comes to finding installed programs:

sub scan_installed { system ('reg query HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\ +Uninstall /s > temp_instal.txt'); open (INSTAL, "<", 'temp_instal.txt') or die "Couldn't open temp_i +nstal.txt for reading: $!\n"; while (<INSTAL>) { if (/DisplayName\s+REG_SZ\s+(.+)/) { push (@installed, $1."\n") unless $1 =~ /Windows XP|KB[0-9 +0-90-9]/; } } close(INSTAL); system ('del temp_instal.txt'); }

My problem is - I want to find out the version (and presence) of Adobe Reader, flash plugin, .NET framework and some other programs. The input data (INSTAL) looks like this:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall +\{AC76BA86-7AD7-1033-7B44-A80000000002} AuthorizedCDFPrefix REG_SZ Comments REG_SZ Contact REG_SZ Customer Support DisplayVersion REG_SZ 8.0.0 HelpLink REG_EXPAND_SZ http://www.adobe.com/support/main.htm +l HelpTelephone REG_SZ InstallDate REG_SZ 20080326 InstallLocation REG_SZ InstallSource REG_SZ C:\SWTOOLS\APPS\Adobe\AdbeRdr80\EN\ ModifyPath REG_EXPAND_SZ MsiExec.exe /I{AC76BA86-7AD7-1033-7 +B44-A80000000002} NoRepair REG_DWORD 0x1 Publisher REG_SZ Adobe Systems Incorporated Readme REG_EXPAND_SZ C:\Program Files\Adobe\Reader 8.0\Reade +r\Readme.htm Size REG_SZ EstimatedSize REG_DWORD 0x13b54 UninstallString REG_EXPAND_SZ MsiExec.exe /I{AC76BA86-7AD7-1 +033-7B44-A80000000002} URLInfoAbout REG_SZ http://www.adobe.com URLUpdateInfo REG_SZ http://www.adobe.com/products/acrobat/r +eadstep.html VersionMajor REG_DWORD 0x8 VersionMinor REG_DWORD 0x0 WindowsInstaller REG_DWORD 0x1 Version REG_DWORD 0x8000000 Language REG_DWORD 0x409 DisplayName REG_SZ Adobe Reader 8

I think that what I need to do, is - for every line beginning with HKEY check the following indented lines for DisplayName, and if it matches a given pattern (Adobe Reader), find the corresponding line with DisplayVersion. But (as in the given example) sometimes DisplayVersion comes first, sometimes it doesn't.

Any ideas on how I could find the version of a given program?

Regards,
Luke

Replies are listed 'Best First'.
Re: Finding installed program version in Win32 registry
by BrowserUk (Patriarch) on Apr 06, 2009 at 10:35 UTC

    There is a useful technique for dealing with this kind of variably ordered input. It revolves around placing captures inside lookahead assertions.

    Because the lookahead assertions do not alter pos, the captured fields are placed into the capture variables ($1, $2, $3 ... ), in the same order, regardless of the order in which that information appears in the input. This is very useful as it greatly simplifies the subsequent processing.

    The following example I've re-ordered the two fields of interest in three copies of the sample data, and the output shows that the two fields (DisplayName & DisplayVersion) have been assigned to $1 & $2 respectively, regarless of their relative positioning in the input record.

    This demo use "paragraph mode", ($/ = '';) which assumes that the records are separated by one or more blank lines:

    #! perl -sw use 5.010; use strict; $/ = ''; ## Paragraph mode while( <DATA> ) { m[ (?= ^ .*? DisplayName \s+ \S+ \s+ ( [^\n]+ ) \n ) (?= ^ .*? DisplayVersion \s+ \S+ \s+ ( [^\n]+ ) \n ) ]xsm and say "Name: $1 Version $2"; }

      Thank you very much for your answer. This is a really useful technique. Regular expressions will never cease to amaze me.

      This leaves just the question of checking just the required few applications. But I think I'll do it by pushing "Name: $1 Version $2" onto a separate list and then, I'll do something along the lines of:

      foreach (@cleanlist) { if (/Adobe Reader.+Version\s+(.+)/) { $gathered_data{'AReaderVer'}=$1; } }

      Maybe it's not the best way, but I think it should work. Thank you very much for your help.

      Luke

        Yes, that will work. But you could avoid the post-filtering step by hard coding the application name into the regex:

        while( <DATA> ) { m[ (?= ^ .*? DisplayName \s+ \S+ \s+ ( Adobe \s Reader \s 8 ) +\n ) (?= ^ .*? DisplayVersion \s+ \S+ \s+ ( [^\n]+ ) \n ) ]xsm and say "Name:$1 Version:$2"; }

        Now, the same information will be captured, but only when the name matches your requirements. If you want to capture the version information for several different apps but exclude any others, then just put the names of those you want to capture into an alternation:

        while( <DATA> ) { m[ (?= ^ .*? DisplayName \s+ \S+ \s+ ( Adobe \s Reader \s 8 | Another \s app \s description | Yet \s Another \s Application ) \n ) (?= ^ .*? DisplayVersion \s+ \S+ \s+ ( [^\n]+ ) \n ) ]xsm and say "Name:$1 Version:$2"; }

        But note: Due to the use of /x on the regex, you will need to explicitly signify any whitespace within the app descriptions using \s (or perhaps \s+).


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Finding installed program version in Win32 registry
by almut (Canon) on Apr 06, 2009 at 10:21 UTC
    sometimes DisplayVersion comes first, sometimes it doesn't

    Maybe you could give Win32::TieRegistry is try instead. It nicely presents stuff as hashes, so order of entries wouldn't matter.