使用 Invoke-Expression 导航复杂/嵌套哈希表的更紧凑/优雅的方式?
A more compact / elegant way to navigate complex / nested hashtables with Invoke-Expression?
我正在研究 acess/modify 嵌套哈希表的函数,通过键层次结构的字符串输入,如下所示:
putH() $hashtable "key.key.key...等等。" “新值”
鉴于:
$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)
我的问题:
有没有更优雅的方法来实现函数的结果...比如使用 invoke-expression?
我试过 invoke-expression - 但无法通过它访问 hassstables(无论组合,嵌套引号)
$s = "k1.k_1.k1_1_1" #custom future input
iex "$c.$s"
returns
System.Collections.Hashtable.k1.k_1.k1_1_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"
我正在研究 acess/modify 嵌套哈希表的函数,通过键层次结构的字符串输入,如下所示:
putH() $hashtable "key.key.key...等等。" “新值”
鉴于:
$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)
我的问题:
有没有更优雅的方法来实现函数的结果...比如使用 invoke-expression?
我试过 invoke-expression - 但无法通过它访问 hassstables(无论组合,嵌套引号)
$s = "k1.k_1.k1_1_1" #custom future input
iex "$c.$s"
returns
System.Collections.Hashtable.k1.k_1.k1_1_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"