powershell - Using start-process in a loop, where some programs will have arguments and some don't; Way to not have if $

I have the below code line, in a script that iterates through things to install from a repository. Some

I have the below code line, in a script that iterates through things to install from a repository. Some things have arguments, some don't. Have come across one that doesn't, and trying to avoid having the same command more or less duplicated (along with all the error checking, etc) in a for...else command. Is there anyway to have start-process work with a $null/empty variable specified after -argumentlist that I am not thinking about?

for($i=0; $i -lt $Installs.count; $i++)
{
  $Arguments = $($Installs[$i].path)

  start-process -filepath $Path -argumentlist $Arguments -wait
}

I have the below code line, in a script that iterates through things to install from a repository. Some things have arguments, some don't. Have come across one that doesn't, and trying to avoid having the same command more or less duplicated (along with all the error checking, etc) in a for...else command. Is there anyway to have start-process work with a $null/empty variable specified after -argumentlist that I am not thinking about?

for($i=0; $i -lt $Installs.count; $i++)
{
  $Arguments = $($Installs[$i].path)

  start-process -filepath $Path -argumentlist $Arguments -wait
}
Share Improve this question edited Jan 29 at 17:40 user66001 asked Jan 29 at 16:53 user66001user66001 9151 gold badge15 silver badges37 bronze badges 0
Add a comment  | 

2 Answers 2

Reset to default 3
  • Your symptom suggests that you're using Windows PowerShell (the legacy, ships-with-Windows, Windows-only edition of PowerShell whose latest and last version is 5.1), where Start-Process's -ArgumentList parameter inexplicably doesn't accept $null, @() (an empty array / enumerable) or '' (the empty string).

  • Fortunately, this unhelpful constraint has been removed in PowerShell (Core) 7, which now interprets any of these inputs as the signal to pass no arguments, i.e. to behave the same as if -ArgumentList hadn't been specified at all. In other words: your code would work as-is there.

However, the splatting technique described in Santiago's answer alone is not enough for a robust solution:

  • A long-standing Start-Process bug that won't be fixed (see GitHub issue #5565) unfortunately requires use of embedded double-quoting around arguments that contain spaces, e.g. -ArgumentList '-foo', '"bar baz"'

Therefore, use the following:

for ($i = 0; $i -lt $Installs.count; $i++)
{
  # Create a hashtable for splatting that originally just contains
  # a value for the -FilePath and -Wait parameters.
  $splat = @{
    FilePath = $Path
    Wait = $true
  }
  # Only add an 'ArgumentList' entry if an install path was given.
  if ($installPath = $Installs[$i].Path) {
    # Enclose the install path in "..." if it contains spaces.
    $splat.ArgumentList = 
      ($installPath, "`"$installPath`"")[$installPath -match ' ']
  }
  # Note the use of @ instead of $ in order to achieve splatting.
  Start-Process @splat
}

Note:

  • The ($installPath = $Installs[$i].Path) conditional:

    • Assigns the value of $Installs[$i].Path to helper variable $installPath...

    • ... and - due to enclosure of the assignment in (...)) - outputs the assigned value, which is then tested using implicit to-Boolean coercion:

    • The value is assumed to be a string, and PowerShell coerces any nonempty string to $true, so that if ($Installs[$i].Path) is in effect shorthand for if ('' -ne $Installs[$i].Path)

  • ($installPath, "`"$installPath`"")[$installPath -match ' '] is an approximation of a ternary conditional, that is, in effect, short for:
    if ($installPath -match ' ') { "`"$installPath`"" } else { $installPath }; see this answer for details.

    • Note that, in PowerShell 7, true ternary conditionals are now available, so you could write,
      $installPath -match ' ' ? "`"$installPath`"" : $installPath
  • Typically you'll also get away with unconditionally double-quoting arguments, which would simplify matters to:
    $splat.ArgumentList = "`"$installPath`""

It's unclear why you're using a for loop instead of a foreach loop, but using splatting should work for your use case, it does require an if condition tho:

for ($i = 0; $i -lt $Installs.count; $i++) {
    $Arguments = @{}
    if ($Installs[$i].path) { $Arguments['ArgumentList'] = $Installs[$i].path }
    Start-Process -FilePath $Path @Arguments -Wait
}

This would be another option too, splatting all arguments and adding the optional one to the splatting hash:

for ($i = 0; $i -lt $Installs.count; $i++) {
    $startProcessSplat = @{
        FilePath = $Path
        Wait     = $true
    }

    if ($Installs[$i].path) {
        $startProcessSplat['ArgumentList'] = $Installs[$i].path
    }

    Start-Process @startProcessSplat
}

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745288714a4620725.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信