Hashtable键语法,用于引用嵌入式哈希表元素

时间:2015-03-13 18:57:11

标签: powershell hashmap hashtable powershell-v2.0

假设我有一个哈希表:

$tokens = @{
    Id=9999; 
    Title="Lorem ipsum dolor sit amet";
    Author=@{Name="John Doe"; Email='john.doe@foo.xyz'};
    Analyst=@{Name="Jane Doe"; Email='jane.doe@foo.xyz'}
}

我想填充的模板,用相应的哈希表的值替换标记(例如__Title__):

/*
Author:     __Author.Name__ <__Author.Email__>
Analyst:    __Analyst.Name__ <__Analyst.Email__>
Request:    __Title__ [__Id__]
*/
...

应该成为:

/*
Author:     John Doe <john.doe@foo.xyz>
Analyst:    Jane Doe <jane.doe@foo.xyz>
Request:    Lorem ipsum dolor sit amet [9999]
*/

有没有办法在父母中引用嵌入式哈希表的元素&#39;哈希表?例如,$tokens['Author.Email']无法正常工作。

代码:

...
return [regex]::Replace( $template, '__(?<tokenName>\w+)__', {
  # __TOKEN__
  param($match)

  $tokenName = $match.Groups['tokenName'].Value

  if ($tokens[$tokenName]) {
    # matching token returns value from hashtable;
    works for simple keys `$tokens['Title']`, not complex keys `$tokens['Author.Name']`
    return $tokens[$tokenName]
  } 
  else {
    # non-matching token returns token
    return $match
  }
})

2 个答案:

答案 0 :(得分:2)

您可以使用点表示法引用元素

$tokens.author.email

如果您想检查名称是否为空,那么您也可以执行此类操作。 注意有一点需要注意:作者应该存在,以便完全按预期工作。)

If(!$tokens.author.name){$tokens.author.name = "Awesome Sauce"; }
Write-Host ("Author Name: {0}" -f $tokens.author.name)

您还可以使用briantist

建议的哈希表符号
$tokens['Author']['Email']

动态替换

你使用动态这个词,但我不确定你想要多远。现在让我们假设$tokens元素都存在,我们将从here-string替换文本。

$text = @"
/*
Author:     __Author.Name__ <__Author.Email__>
Analyst:    __Analyst.Name__ <__Analyst.Email__>
Request:    __Title__ [__Id__]
*/
"@

$text -replace "__Author\.Name__",$tokens.Author.Name -replace "__Author\.Email__",$tokens.Author.Email `
        -replace "__Analyst\.Name__",$tokens.Analyst.Name -replace "__Analyst\.Email__",$tokens.Analyst.Email `
        -replace "__Title__",$tokens.Title -replace "__Id__",$tokens.Id

但我觉得你的意思是更多动态,因为所有这些都需要知道有关$Tokens和源字符串的信息。让我知道我们现在的立场。我们可以更深入地了解这一点。

让我们变得怪异

假设您知道哈希表$tokens和源$text具有共同的值,但您不知道它们的名称。这将根据哈希表上的键名动态填充文本。目前,只有在只有一个散列表深度时才有效。

ForEach($childKey in $tokens.Keys){ 
    If($tokens[$childKey] -is [System.Collections.Hashtable]){
        ForEach($grandChildKey in $tokens[$childKey].Keys){ 
            Write-Host "GrandChildKey = $childKey"
            $text = $text -replace "__$childKey\.$($grandChildKey)__", $tokens.$childKey.$grandChildKey
        }
    } Else {
        $text = $text -replace "__$($childKey)__", $tokens.$childKey
    }
}

$text

其他

这借鉴了mike z关于Invoke-Expression的建议,因为它减少了猜测工作。

$output = $text
$placeHolders = $text | Select-String '__([\w.]+)__' -AllMatches | ForEach-Object{$_.matches} | ForEach-Object{$_.Value}
$placeHolders.count
$placeHolders | ForEach-Object {
    $output = $output -replace [regex]::Escape($_), (Invoke-Expression "`$tokens.$($_ -replace "_")")
}
$output

$text中搜索所有字符串,例如某事。对于每个匹配,用等号的点符号替换该文本。

任何一个样本的输出应该与应该成为:

的输出相匹配

答案 1 :(得分:2)

一些事情:

  1. 您需要修复正则表达式以实际匹配嵌套属性。现在你不需要它__(?<tokenName>[\w\.]+)__

  2. 使用Invoke-Expression动态展开嵌套属性。只需构建一个表示要评估的表达式的字符串。这很好,因为它不依赖于模型对象$tokens及其属性,完全是哈希表。它所需要的只是让属性在那里的对象上解析。

  3. 下面是一个简短的例子。注意:如果模板来自不安全的来源,请注意这一点并首先清理输入:

    $tokens = @{
        Id=9999; 
        Title="Lorem ipsum dolor sit amet";
        Author=@{Name="John Doe"; Email='john.doe@foo.xyz'};
        Analyst=@{Name="Jane Doe"; Email='jane.doe@foo.xyz'};
        '3PTY' = "A";
        Test=@{'Name with space' = 'x' }
    }
    
    $template = @"
    /*
    Author:     __Author.Name__ <__Author.Email__>
    Analyst:    __Analyst.Name__ <__Analyst.Email__>
    Request:    __Title__ [__Id__]
    3PTY: __"3PTY"__
    Name:__Test.'Name with space'__
    */
    "@
    
    function Replace-Template {
    
        param ([string]$template, $model)
    
        [regex]::Replace( $template, '__(?<tokenName>[\w .\''\"]+)__', {
          # __TOKEN__
          # Note that TOKEN should be a valid PS property name. It may need to be enclosed in quotes
          # if it starts with a number or has spaces in the name. See the example above for usage.
          param($match)
    
          $tokenName = $match.Groups['tokenName'].Value
          Write-Verbose "Replacing '$tokenName'"
          $tokenValue = Invoke-Expression "`$model.$tokenName" -ErrorAction SilentlyContinue
    
          if ($tokenValue) {
            # there was a value. return it.
            return $tokenValue
          } 
          else {
            # non-matching token returns token
            return $match
          }
        })
    }
    
    Replace-Template $template $tokens
    

    输出:

      

    / *
      作者:John Doe
      分析师:Jane Doe   要求:Lorem ipsum dolor sit amet [9999]
      3PTY:A
      名称:X
      * /