【问题标题】:A more compact / elegant way to navigate complex / nested hashtables with Invoke-Expression? [duplicate]使用 Invoke-Expression 导航复杂/嵌套哈希表的更紧凑/优雅的方式? [复制]
【发布时间】:2021-02-15 07:13:40
【问题描述】:

我正在开发一个函数来通过键层次结构的字符串输入来访问/修改嵌套哈希表,如下所示:
putH() $hashtable "key.key.key...etc." “新价值”

给定:

$c = @{
       k1 = @{
              k1_1 = @{
                      k1_1_1 = @{ key = "QQQQQ"}
                      }
             }
      }

到目前为止,我已经想出了这个修改值的函数:

function putH ($h,$hKEYs,$nVAL){
    if ($hKEYs.count -eq 1) {               
        $bID = $hKEYs                             #match the last remaining obj in $hkeys
    }
    else {
        $bID = $hKEYs[0]                          #match the first obj in $hekys
    }
    foreach ($tk in $h.keys){
        if ($tk -eq $bID){
            if ($hKEYs.count -eq 1){              #reached the last obj in $hkeys so modify
                $h.$tk = $nVAL
                break
            }  
            else {                                
                $trash,$hKEYs = $hKEYs                #take out the first obj in $hkeys
                $h.$tk = putH $h.$tk $hKEYs $nVAL     #call the function again for the nested hashtale
                break
            }
        }
    } 
return $h
}

还有这个获取值的函数:

function getH ($h,$hKEYs){
if ($hKEYs.count -eq 1) {
    $bID = $hKEYs
}
else {
    $bID = $hKEYs[0]
}
foreach ($tk in $h.keys){
    if ($tk -eq $bID){
        if ($hKEYs.count -eq 1){
            $h = $h.$tk
            break
        }
        else {
        $trash,$hKEYs = $hKEYs
        $h = getH $h.$tk $hKEYs
        break
        }
    }
}
return $h
}

我是这样使用的:

$s = "k1.k_1.k1_1_1"   #custom future input
$s = $s.split(".")
putH $c ($s) "NEW_QQQQQ" 
$getval = getH $c ($s)

我的问题:
有没有更优雅的方法来实现函数的结果...比如调用表达式?
我已经尝试过调用表达式 - 但无法通过它访问 hassstables(无论组合、嵌套引号如何)

$s = "k1.k_1.k1_1_1"   #custom future input
iex "$c.$s"
    

返回

System.Collections.Hashtable.k1.k_1.k1_1_1

【问题讨论】:

  • 你需要转义$c:"`$c.$s"
  • 谢谢!三天以来,我一直在脑海中。如果我理解正确:通过不转义第一个 $-iex 在 .然后尝试查找整个字符串作为键;并通过转义它 - 扩展 $s 然后整个结果字符串
    我对吗?
  • 如果您转义$c,双引号字符串将导致两个变量展开之前 iex可以做任何事情.

标签: powershell nested hashtable


【解决方案1】:

不要使用Invoke-Expression

我会在底部回答你的问题,但我觉得有必要指出调用Invoke-Expression here is both dangerous,更重要的是,没有必要

您可以解决整个嵌套成员引用链,只需将“路径”拆分为各个部分 ('A.B.C' -> @('A', 'B', 'C')),然后逐个取消引用它们(您甚至不需要递归!):

function Resolve-MemberChain 
{
  param(
    [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
    [psobject[]]$InputObject,

    [Parameter(Mandatory = $true, Position = 0)]
    [string[]]$MemberPath,

    [Parameter(Mandatory = $false)]
    [ValidateNotNullOrEmpty()]
    [string]$Delimiter = '.'
  )

  begin {
    $MemberPath = $MemberPath.Split([string[]]@($Delimiter))
  }

  process {
    foreach($o in $InputObject){
      foreach($m in $MemberPath){
        $o = $o.$m
      }
      $o
    }
  }
}

现在您可以在没有iex 的情况下解决您的问题:

$ht = @{
  A = @{
    B = @{
      C = "Here's the value!"
    }
  }
}

$ht |Resolve-MemberChain 'A.B.C' -Delimiter '.'

您可以使用相同的方法更新嵌套成员值 - 只需在最后一步停止,然后分配给$parent.$lastMember

function Set-NestedMemberValue
{
  param(
    [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
    [psobject[]]$InputObject,

    [Parameter(Mandatory = $true, Position = 0)]
    [string[]]$MemberPath,

    [Parameter(Mandatory = $true, position = 1)]
    $Value,

    [Parameter(Mandatory = $false)]
    [ValidateNotNullOrEmpty()]
    [string]$Delimiter = '.'
  )

  begin {
    $MemberPath = $MemberPath.Split([string[]]@($Delimiter))
    $leaf = $MemberPath |Select -Last 1
    $MemberPath = $MemberPath |select -SkipLast 1
  }

  process {
    foreach($o in $InputObject){
      foreach($m in $MemberPath){
        $o = $o.$m
      }
      $o.$leaf = $Value
    }
  }
}

在行动中:

PS ~> $ht.A.B.C
Here's the value!
PS ~> $ht |Set-NestedMemberValue 'A.B.C' 'New Value!'
PS ~> $ht.A.B.C
New Value!

为什么您当前的方法不起作用?

您当前的实现面临的问题是$c.$s 中的$c 在评估字符串文字"$c.$s" 后立即展开 - 为避免这种情况,只需转义第一个$

iex "`$c.$s"

【讨论】:

  • @kincuza 抱歉,如果之前不清楚,我现在添加了一个完整示例,说明如何使用相同的技术设置 值。如果避免iex 不优雅,那我恐怕不知道你在问什么:-)
  • 不要误会我的意思——即使我还没有完全理解它,我也很欣赏你的回答——我是 PowerShell(和一般编程)的初学者,并且可能有不同的优雅概念和代码的简单性 - 我主要使用 PowerShell 脚本(只有我调用)来更新我个人计算机上的一些设置文件 - 这就是为什么我不介意安全风险并且它更方便(是的 - 更优雅)我使用单行 iex 而不是函数。 (我创建的功能是因为无法使用我常用的 iex 方法而感到沮丧)
猜你喜欢
  • 2012-05-08
  • 2014-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-16
  • 1970-01-01
  • 2013-12-06
  • 1970-01-01
相关资源
最近更新 更多