1

I'm trying to write to a .js file based on the contents of a text file (C:\installedApps.txt) using PowerShell.

The installedApps.txt file contains the name and version of installed applications on a computer - I want to search installedApps.txt for an application name and version and have the result assigned to the $findAppName and $findAppVer variables respectively.

I then want to write to a .js file IF the Select-String cmdlet is successful in matching the $appName and $appVer arguments passed into the Check-Install function. See code below:

function Check-Install ([string]$appName, [string]$appVer) {

  $findAppName = Get-Content C:\installedApps.txt | Select-String $appName
  $findAppver = Get-Content C:\installedApps.txt | Select-String $appVer

  if ($findAppName -and $findAppVer -eq $true) {
   Set-Content -path $jsFile -Value "var row = getElementById('row-1'); 
   row.classList.add('hidden');"    
   } else { 
     write-host Match Not Found
   }
}

Check-Install -appName "Cisco AnyConnect Secure Mobility Client" -appVer "4.6.01103"

The $findAppName and $findAppVer variables aren't returning anything, but not sure where I'm going wrong - the Select-String cmdlet should accept variables(?).

Note: No errors thrown up in the shell

UPDATE:

Here's an example of how the installedApps.txt log is formatted:

DisplayName     : 7-Zip 18.05 (x64)
Publisher       : Igor Pavlov
InstallDate     : 
DisplayVersion  : 18.05
UninstallString : C:\Program Files\7-Zip\Uninstall.exe

DisplayName     : Adobe Flash Player 31 NPAPI
Publisher       : Adobe Systems Incorporated
InstallDate     : 
DisplayVersion  : 31.0.0.122
UninstallString : 
C:\WINDOWS\SysWOW64\Macromed\Flash\FlashUtil32_31_0_0_122_Plugin.exe - 
maintain plugin

DisplayName     : Adobe Flash Player 31 PPAPI
Publisher       : Adobe Systems Incorporated
InstallDate     : 
DisplayVersion  : 31.0.0.122
UninstallString : 
C:\WINDOWS\SysWOW64\Macromed\Flash\FlashUtil32_31_0_0_122_pepper.exe - 
maintain pepperplugin

DisplayName     : Cisco AnyConnect Secure Mobility Client
Publisher       : Cisco Systems, Inc.
InstallDate     : 20180906
DisplayVersion  : 4.6.01103
UninstallString : MsiExec.exe /X{58524593-122C-43F0-96E2-A6BCC42E3412}

I want to trigger an application install/uninstall (without using the WMI object, as I've read it takes a while and can trigger an installation repair on query) and write to a local web page based on the $findApp results via a .js file to show whether the application is already installed or not.

I'm trying to create a software update script with a front-end written in HTML, CSS and JavaScript for the UI. The script will run on logon across multiple computers in a domain. I'll need to use Ajax to find a way to update the local web page dynamically without a page refresh, but one step at a time.

pmilo
  • 21
  • 6
  • 1
    You do not return `$findAppName` or `$findAppver`. The variables are only visible in the scope of the function. Can you please clearify the expected and actual behaviour? – vrdse Nov 25 '18 at 19:42
  • 1
    Presumably your file `installedApps.txt` contains AppName and AppVer on one line, yoru two separate select-string might find these also on two separate lines. Better use a RegEx for a match on one line. Also your title doesn't seem to be related to your text/code. –  Nov 25 '18 at 20:10

1 Answers1

1

Try the following:

Note that the assumption is that the input *.txt file's lines contain the application name before the version number, but it's easy to change that.

function Check-Install ([string]$appName, [string]$appVer) {

  if (Select-String -Quiet -Pattern (
         '\b{0}\b{1}\b' -f [regex]::Escape($appName), [regex]::Escape($appVer)
       ) -LiteralPath C:\installedApps.txt
  ) { # if
    Set-Content -path $jsFile -Value "var row = getElementById('row-1');
    row.classList.add('hidden');"
  } else {
     write-host 'No match.'
  }

}

Check-Install -appName "Cisco AnyConnect Secure Mobility Client" -appVer "4.6.01103"
  • Select-String -Quiet outputs only a [bool] value that reflects in the abstract whether the pattern matched (at least) one line.

  • [regex]::Escape() is used to escape the application name and version for use in a regex so that they're treated as literals when matching.

  • '\b{0}\b{1}\b' -f ... builds a single regex (string) from the escaped values to look for both values on the same line, using assertion \b to ensure that they only match at word boundaries (and not also inside larger words).

  • Note how Select-String can directly accept a filename operand, via the -LiteralPath or -Path parameters, which is both shorter and more efficient.


As for what you tried:

$findAppName = Get-Content C:\installedApps.txt | Select-String $appName
$findAppver = Get-Content C:\installedApps.txt | Select-String $appVer
  • You're looking for the name and version separately, which means that you may find a match for a version number that pertains to a different application, for instance.

  • $findAppName and $findAppver are either "nothing" (effectively, $null), if no match is found, or instances of type [MatchInfo]

$findAppName -and $findAppVer -eq $true
  • As stated, these variables aren't Booleans, so comparing them with -eq to $true is pointless.

Apart from that, due to PowerShell's operator precedence, your statement would be parsed as $findAppName -and ($findAppVer -eq $true), i.e., the explicit -eq comparison is only applied to the second variable, which in your case always returns $false.

That said, you could have relied on PowerShell's implicit Boolean logic[1] (leaving the problem of searching for the terms separately aside):

$findAppName -and $findAppVer

This would have evaluated to $true if both $findAppName and $findAppVer contained [MatchInfo] instance(s), i.e., if both Select-String calls had found at least 1 match.


[1] For a summary of PowerShell's implicit to-Boolean conversion logic, see this answer.

mklement0
  • 382,024
  • 64
  • 607
  • 775