为什么Powershell -replace会更改对象的类型

时间:2018-01-08 19:59:23

标签: powershell

我对我看到的一些行为感到有些困惑。

以下代码旨在查找和替换注册表中的多个字符串。

$keys = @(gci -Path hkcu:\ -recurse -ErrorAction SilentlyContinue)
foreach ($key in $keys)
{
    foreach ($vname in $key.GetValueNames())
    {
        $val = $key.GetValue($vname, $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
        if ($val -like "c:\users\administrator*")
        {
            $nval = $val -replace "(?i:^(C:\\users\\Administrator))", "%USERPROFILE%"
            write-host "$key\$vname=$val -> $nval"
            ((Get-Item $Key.PSParentPath).OpenSubKey($Key.PSChildName, "True")).SetValue($vname, $nval, $key.GetValueKind($vname)) 
        }
    }
}

当注册表值类型为REG_MULTI_SZ时,我继续收到以下错误消息。 Exception calling "SetValue" with "3" argument(s): "The type of the value object did not match the specified RegistryValueKind or the object could not be properly converted."

如果我注释掉-replace部分,即:

    $nval = $val #-replace "(?i:^(C:\\users\\Administrator))", "%USERPROFILE%"

注册表项更新时没有错误(显然具有相同的值)。

因此,-replace操作中的某些内容正在改变数据类型。它似乎改为类型System.Object []。

registryKey.SetValue方法需要字符串[]来设置REG_MULTI_SZ。

为什么要更改类型?我该如何解决这种问题呢?

更新 在答案中应用选项#1不起作用。即使在添加演员之后,$nval仍然是System.Object[]类型。应用选项#3确实有效。

以下是正确搜索并替换注册表中字符串开头的字符串的最终代码。

$keys = @(gci -Path hkcu:\ -recurse -ErrorAction SilentlyContinue)
foreach ($key in $keys)
{
    foreach ($vname in $key.GetValueNames())
    {
        $val = $key.GetValue($vname, $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
        if ($val -like "c:\users\administrator*")
        {
            if ($key.GetValueKind($vname) -eq [Microsoft.Win32.RegistryValueKind]::MultiString)
                { $nval = $val -replace "(?i:^(C:\\users\\Administrator))", "%USERPROFILE%" -as [string[]] }
            else
                { $nval = $val -replace "(?i:^(C:\\users\\Administrator))", "%USERPROFILE%" }

            write-host "$key\$vname=$val -> $nval"
            ((Get-Item $Key.PSParentPath).OpenSubKey($Key.PSChildName, "True")).SetValue($vname, $nval, $key.GetValueKind($vname)) 
        }
    }
}

2 个答案:

答案 0 :(得分:4)

PowerShell中返回多个对象的大多数内容都将它们返回为[object[]];很可能是因为你可以返回任意数量的不同对象,并且因为PowerShell包装[PSObject]中的大多数对象,无论你是否知道它。

因此,虽然您可以从[string[]]开始,但修改它会产生[object[]]

当你需要一个特定类型的数组时,简单的方法就是抛出它。

您可以使用3个选项进行投射:转换赋值或转换变量,或使用-as运算符。

投射值:

$nval = [string[]]($val -replace "(?i:^(C:\\users\\Administrator))", "%USERPROFILE%")

现在$nval将是[string[]],除非您稍后再为其分配其他内容。

投射变量:

[string[]]$nval = $val -replace "(?i:^(C:\\users\\Administrator))", "%USERPROFILE%"

这有点不同。将强制转换放在PowerShell中的变量上会将该强制转换应用于分配给它的所有值。

如果您知道$nval始终需要[string[]]并且您不想在每项作业中投放它,这是一个不错的选择。

-As运营商:

$nval = $val -replace "(?i:^(C:\\users\\Administrator))", "%USERPROFILE%" -as [string[]]

这类似于投射价值;不同之处在于,如果演员表不成功,它不会抛出异常,只会返回$null

为了处理不同的注册表种类和所需的演员表,我在评论中推荐了switch语句,但我想出了一个更好的主意:只需使用哈希表:

$kindToType = @{
    [Microsoft.Win32.RegistryValueKind]::MultiString = [string[]]
    [Microsoft.Win32.RegistryValueKind]::ExpandString = [string]
    [Microsoft.Win32.RegistryValueKind]::String = [string]
    [Microsoft.Win32.RegistryValueKind]::DWord = [int]
    # etc.
}

然后:

$nval = $val -replace "(?i:^(C:\\users\\Administrator))", "%USERPROFILE%" -as $kindToType[$key.GetValueKind($vname)]

这将创建一个查找表,您可以在其中直接从注册表类型中获取类型。

答案 1 :(得分:2)

使用重点摘要补充briantist's helpful answer

  • 如果-replace表达式的LHS是集合 [1] ,替换是在的每个元素上进行的

  • 结果以常规PowerShell数组的形式返回,其类型为[System.Object[]] - ,与输入集合的具体类型无关

  • 要保留输入集合类型,您必须使用显式强制转换或调用适当的构造函数 - 请参阅briantist的答案。

示例:

# Apply -replace to a LHS of type [System.Collections.ArrayList]
$result = ([System.Collections.ArrayList] ('one', 'two')) -replace 'o', '@'

> $result
@ne
tw@

> $result.GetType().FullName # inspect the result's type
System.Object[]

[1]没有查看源代码,似乎"集合"在这个意义上,指任何实现接口[System.Collections.IEnumerable]的类型,它不会实现[System.Collections.IDictionary];对于给定的实例$coll,您可以按如下方式进行测试:
$coll -is [System.Collections.IEnumerable] -and $coll -isnot [System.Collections.IDictionary]
值得注意的是,此排除类似哈希表的集合,例如[hashtable](文字语法@{ ... })和[System.Collections.Specialized.OrderedDictionary](文字语法[ordered] @{ ... })。