Konrad Viltersten
Konrad Viltersten

Reputation: 39148

Executing a file that may reside at two different locations using PowerShell

I have a cute little script in my $PROFILE helping me start Notepad++ from the script showing a file of my choosing.

function Edit{
  param([string]$file = " ")
  Start-Process "C:\Program Files\Notepad++\notepad++.exe" -ArgumentList $file
}

It's worked great until recently, where I jump between different systems. I discovered that NPP is installed in C:\Program Files on some systems but in C:\Program Files (x86) on others. I can edit the script adapting it but having done so a gazillion times (i.e. 5 to this point), I got sick and tired of it, realizing that I have to automate this insanity.

Knowing little about scripting, I wonder what I should Google for. Does best practice dictate using exception handling in such a case or is it more appropriate to go for conditional expressions?

According to Get-Host | Select-Object Version I'm running version 5.1, if it's of any significance. Perhaps there's an even neater method I'm unaware of? Relying on an environment variable? I'd also prefer to not use a method valid in an older version of PS, although working, if there's a more convenient approach in a later one. (And given my experience on the subject, I can't tell a duck from a goose.)

Upvotes: 3

Views: 283

Answers (3)

mklement0
mklement0

Reputation: 438273

Let me offer a streamlined version that also supports passing multiple files:

function Edit {

  param(
   # Allow passing multiple files, both with explicit array syntax (`,`-separated)
   # or as indiv. arguments.
   [Parameter(ValueFromRemainingArguments)]
   [string[]] $File
  )

  # Construct the potential Notepad++ paths.
  # Note: `-replace '$'` is a trick to append a string to each element
  #       of an array.
  $exePaths = $env:ProgramFiles, ${env:ProgramFiles(x86)} -replace '$', '\Notepad++\notepad++.exe'

  # See which one, if any, exists, using Get-Command.
  $exeToUse = Get-Command -ErrorAction Ignore $exePaths | Select-Object -First 1

  # Fall back to Notepad.
  if (-not $exeToUse) { $exeToUse = 'notepad.exe' }

  # Invoke whatever editor was found with the optional file(s).
  # Note that both Notepad++ and NotePad can be invoked directly
  # without blocking subsequent commands, so there is no need for `Start-Process`,
  # whose argument processing is buggy.
  & $exeToUse $File

}
  • An array of potential executable paths is passed to Get-Command, which returns a command-info object for each actual executable found, if any.

    • -ErrorAction Ignore quietly ignores any errors.

    • Select-Object -First 1 extracts the first command-info object, if present, from the Get-Command output; this is necessary to guard against the (perhaps unlikely) case where the executable exists in both locations.

  • $exeToUse receives $null (effectively) if Get-Command produces no output, in which case Boolean expression -not $exeToUse evaluates to $true, causing the fallback to notepad.exe to take effect.

  • Both command names (strings) and command-info objects (instances of System.Management.Automation.CommandInfo or derived classes, as returned by Get-Command) can be executed via &, the call operator.

Upvotes: 1

Booga Roo
Booga Roo

Reputation: 1781

I would use conditionals for this one. One option is to test the path directly if you know for certain it is in a particular location.

Hard coded paths:

function Edit{
  param([string]$file = " ")

$32bit = "C:\Program Files (x86)\Notepad++\notepad++.exe"
$64bit = "C:\Program Files\Notepad++\notepad++.exe"

if (Test-Path $32bit) {Start-Process -FilePath $32bit -ArgumentList $file}
elseif (Test-Path $64bit) {Start-Process -FilePath $64bit -ArgumentList $file}
else {Write-Error -Exception "NotePad++ not found."}
}

Another option is pulling path information from registry keys, if they're available:

function Edit{
  param([string]$file = " ")

$32bit = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Notepad++\' -ErrorAction SilentlyContinue).("(default)")
$64bit = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\WOW6432Node\Notepad++\' -ErrorAction SilentlyContinue).("(default)")

if ($32bit) {Start-Process -FilePath "$32bit\notepad++.exe" -ArgumentList $file}
elseif ($64bit) {Start-Process -FilePath "$64bit\notepad++.exe" -ArgumentList $file}
else {Write-Error -Exception "NotePad++ not found."}
}

Upvotes: 3

Konrad Viltersten
Konrad Viltersten

Reputation: 39148

Based on the great help from @BoogaRoo (who should get some +1 for effort) and asked by the same to post my own version of the answer, I go against my reluctance to post asnwers to own questions due to strong sensation of tackiness.

My final version, taking into account systems that lack NP++ but still want to show the editor of some kind.

function Edit{
  param([string]$file = " ")

  $executable = "Notepad++\notepad++.exe"
  $32bit = "C:\Program Files (x86)\" + $executable
  $64bit = "C:\Program Files\" + $executable
  $target = "notepad"

  if(Test-Path $32bit) { $target = $32bit }
  if(Test-Path $64bit) { $target = $64bit }

  Start-Process $target -ArgumentList $file
}

Upvotes: 1

Related Questions