为什么我的条件 (-not A -and (A -or B -or C)) 不起作用?

Why doesn't my condition (-not A -and (A -or B -or C)) work?

我写了 _in 函数来检测我们是否必须安装软件包。参数 -packages+packages 有效,但 +base+full 无效,我该如何解决?

$scriptArgs=$args

function _in {
  Param($find)

  foreach ($i in $scriptArgs) {
    if ($i -eq $find) {
      return 1
    }
  }
  return 0
}

# Install packages
if (-not (_in("-packages")) -and (_in("+packages") -or _in("+base") -or _in("+full"))) {
  PrintInfo "* Installing packages"
}

这个有效:

PS> powershell .\scripts\win\install_zds.ps1 +packages
* Installing packages
PS> powershell .\scripts\win\install_zds.ps1 +packages -packages

-packages 禁用包安装,+packages 启用包安装。

这行不通:

PS> powershell .\scripts\win\install_zds.ps1 +base
PS> powershell .\scripts\win\install_zds.ps1 +full

+base+full 应该启用软件包安装。


编辑: 我想了解原因:

我关注评论,然后,我删除括号如下:

if (-not (_in "-packages") -and ((_in "+packages") -or (_in "+base") -or (_in "+full"))) { }

这行得通,但我不明白为什么。我 found this explain 关于 PowerShell 中的括号:

Powershell is a parsed and interpreted language. The interpreter see's parenthesis as a control structure and is not expected or required at the Call Site.

但是对于 test-function("Hello")hello 是字符串而不是结构。

function Test-Function {
  Param(
    [string]
    $hello
  )

  "String: $hello"
}

Test-Function("Hello")
Test-Function "Hello"

表达式

-not (_in("-packages")) -and (_in("+packages") -or _in("+base") -or _in("+full"))

未按照您显然期望的方式进行评估。

PowerShell 函数(与方法调用不同)期望它们的参数是不带括号的空格分隔列表,即 _in("foo") 应该是 _in "foo"。括号在语法上没有错误(_in("foo") 是一个有效的表达式),但 PowerShell 会将括号解析为分组表达式,首先对其求值。这意味着 PowerShell 在实际调用函数之前首先将 _in("foo") 扩展为 _in "foo"

但是,由于您将函数调用放在布尔表达式中,因此您需要在每个函数调用周围放置分组括号,以便首先计算函数调用,以便在布尔表达式中使用函数调用的结果:

(_in "foo") -and (_in "bar")

否则,布尔运算符将被解析为第一个函数的参数。也就是说

_in("foo") -and _in("bar")

将扩展为

_in "foo" -and _in "bar"

然后将使用参数 foo-and_inbar.

调用函数 _in()

因此你的条件必须写成

-not (_in "-packages") -and ((_in "+packages") -or (_in "+base") -or (_in "+full"))

话虽如此,您尝试实现的不仅会重新实现 -in/-contains 运算符,而且还违反了正常的 PowerShell 参数处理。我强烈建议您查看 advanced function parameters 和参数集。它们在函数和脚本级别上工作。

示例:

[CmdletBinding(DefaultParameterSetName='none')]
Param(
    [Parameter(ParameterSetName='base', Mandatory=$true)]
    [Switch]$Base,

    [Parameter(ParameterSetName='full', Mandatory=$true)]
    [Switch]$Full
)

switch ($PSCmdlet.ParameterSetName) {
    'none' { 'install nothing' }
    'base' { 'base install' }
    'full' { 'full install' }
}

请注意,Powershell 在涉及 -and 和 -or 时非常不寻常,它们具有相同的优先级。大多数其他语言不是这样的(C#、vbscript...)。似乎一开始就被忽略了,现在他们不想破坏现有的脚本。

$true -or $true -and $false
False

$true -or ($true -and $false)
True

这是更典型的行为,带有 + 和 *。 * 比 + 具有更高的优先级。

1 + 2 * 3
7

(1 + 2) * 3
9