【问题标题】:Powershell console different to scriptPowershell 控制台不同于脚本
【发布时间】:2021-11-16 00:53:22
【问题描述】:

谁能告诉我为什么这个命令在 Powershell 控制台中运行良好,返回一个指纹,但是当作为脚本运行时,它只返回所有证书的指纹:

$crt = (Get-ChildItem -Path Cert:\LocalMachine\WebHosting\ | Where-Object {$_.Subject.Contains($certcn)}).thumbprint

$certcn 是一个包含域的字符串。例如“www.test.com”

【问题讨论】:

  • $certcn 没有在任何地方定义

标签: powershell parameter-passing tostring


【解决方案1】:

我想通了。 $certcn 源自 $args[0]。事实证明 $args[0] 不是字符串,即使 PS 很乐意在其他命令中将其用作字符串,但它不会使用 Where-Object 执行此操作。 不确定 $args[0] 实际上是什么类型,但是 $certcn = $args[0].tostring() 修复了它。

【讨论】:

  • 请考虑将您的答案标记为您问题的解决方案。
  • 重新找出 $args[0] 实际上是什么类型:使用 $args[0].GetType().FullName。要了解类型的成员,请将实例传递给 Get-Member: Get-Member -InputObject $args[0]。知道实际类型是什么会很有趣,因为它似乎被转换为 空字符串,这是不寻常的。
【解决方案2】:

您的症状的唯一解释是变量$certcn的值

  • 或者: 空字符串 ('') 因为'someString'.Contains('')任何返回$true em> 输入字符串。

  • 或:隐式转换为空字符串,尽管这在实践中不会经常发生;以下是一些示例(请参阅GitHub issue # 了解下面提到的[pscustomobject] 字符串化错误):

    # An empty array stringifies to ''
    'someString'.Contains(@()) # -> $true 
    
    # A single-element array containing $null stringifies to ''
    'someString'.Contains(@($null)) # -> $true 
    
    # Due to a longstanding bug, [pscustomobject] instances, when
    # stringified via .ToString(), convert to the empty string. 
    # This makes the command equivalent to `.Contains(@(''))`, which is again
    # the same as `.Contains('')`
    'someString'.Contains(@([pscustomobject] @{ foo=1 })) # -> $true 
    

$args[0] 不是字符串

automatic $args variable 是一个数组,其中包含所有未绑定到声明的参数(如果有)的位置参数。

$args 可以包含任何数据类型的元素,而该类型是由调用者单独确定。

但是,如果你正式声明一个参数,你可以键入,这意味着如果调用者传递一个不同的参数数据类型,尝试将参数转换为参数的类型(这可能会失败,但至少失败会“响亮”,原因很明显)。


为您的脚本提供强大的解决方案:

param(
  [Parameter(Mandatory)] # Ensure that the caller passes a value.
  [string] $CertCN       # Type-constrain to a string.
  # , ... declare other parameters as needed
)

# $CertCN is now guaranteed to be a *string* that is *non-empty*.
$crt = 
  (Get-ChildItem -Path Cert:\LocalMachine\WebHosting | 
     Where-Object { $_.Subject.Contains($CertCN) }).thumbprint

注意:

  • 参数声明块中的use of the [Parameter()] attribute (param(...)) 使您的脚本成为advanced one,这意味着不支持$args,需要所有 参数显式绑定声明的参数;但是,如果需要,您可以使用 [Parameter(ValueFromRemaningArguments)] 定义一个包罗万象的参数。 (使脚本或函数成为高级脚本或函数的另一件事是在整个 param(...) 块上方使用 [CmdletBinding()] 属性。)

  • [Parameter(Mandatory)],除了确保调用者传递参数的值外,还隐式地阻止传递 empty 字符串(或 $null) - 尽管您可以使用 @ 显式允许它987654345@

  • 此外,高级脚本和函数会自动阻止将 数组 传递给 [string] 类型的参数,这是可取的。 (相比之下,simple 函数和脚本只是 stringify 数组,就像在可扩展字符串(字符串插值)中发生的那样;例如,& { param([string] $foo) $foo } 1, 2 绑定 '1 2',这也是"$(1, 2)" 会得到什么)


警告

将值传递给[string] 类型的参数时,PowerShell接受任何类型的标量(非集合),并且任何非字符串类型都通过.ToString()自动转换为字符串。这通常是可取和方便的,但会导致无用的字符串化;例如:

& { param([string] $str) $str } @{} # -> 'System.Collections.Hashtable'

hashtables (@{ ... }) 的实例将其字符串化为它们的类型名称,这是没有帮助的,并且这种行为是任何没有显式实现有意义的字符串表示的类型的默认行为通过覆盖.ToString() 方法。

如果这是一个问题,您可以修改您的脚本以确保传递的参数值已经是一个字符串,使用[ValidateScript()] 属性。 p>

param(
  [Parameter(Mandatory)]
  # Ensure that the argument value is a [string] to begin with.
  # Note: The `ErrorMessage` property requires PowerShell (Core) 7+
  [ValidateScript({ $_ -is [string] }, ErrorMessage='Please pass a *string* argument.')]
  $CertCN  # Do not type-constrain, so that the original type can be inspected.
  # , ... declare other parameters as needed
)

# ...

不幸的是,正如代码 cmets 中所述,在需要 PowerShell (Core) 7+ 中使用 ErrorMessage 属性。在 Windows PowerShell 中总是会显示标准错误消息,这根本不是用户友好的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-29
    • 1970-01-01
    • 2020-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多