PowerShell - 将 属性 名称从 Pascal 大小写转换为带下划线的大写

PowerShell - Convert Property Names from Pascal Case to Upper Case With Underscores

假设我有一个这样的对象:

$test = @{
          ThisIsTheFirstColumn = "ValueInFirstColumn"; 
          ThisIsTheSecondColumn = "ValueInSecondColumn"
         }     

我想结束:

$test = @{
          THIS_IS_THE_FIRST_COLUMN = "ValueInFirstColumn"; 
          THIS_IS_THE_SECOND_COLUMN = "ValueInSecondColumn"
         } 

无需手动对新列名称进行编码。

这显示了我想要的值:

$test.PsObject.Properties | where-object { $_.Name -eq "Keys" } | select -expand value | foreach{ ($_.substring(0,1).toupper() + $_.substring(1) -creplace '[^\p{Ll}\s]', '_$&').Trim("_").ToUpper()} | Out-Host

这导致:

THIS_IS_THE_FIRST_COLUMN
THIS_IS_THE_SECOND_COLUMN

但现在我似乎无法弄清楚如何将这些新值分配回对象。

这似乎有效。不过,我最终把东西放在了一个新的哈希表中。

$test = @{
          ThisIsTheFirstColumn = "ValueInFirstColumn"; 
          ThisIsTheSecondColumn = "ValueInSecondColumn"
         }  
$test2=@{}
$test.PsObject.Properties | 
    where-object { $_.Name -eq "Keys" } | 
    select -expand value | foreach{ $originalPropertyName=$_
                                    $prop=($_.substring(0,1).toupper() + $_.substring(1) -creplace '[^\p{Ll}\s]', '_$&').Trim("_").ToUpper()
                                    $test2.Add($prop,$test[$originalPropertyName])
                                  } 
$test2

您可以按如下方式就地修改哈希表$test

foreach($key in @($test.Keys)) { # !! @(...) is required - see below.
  $value = $test[$key] # save value
  $test.Remove($key)   # remove old entry
  # Recreate the entry with the transformed name.
  $test[($key -creplace '(?<!^)\p{Lu}', '_$&').ToUpper()] = $value
}

@($test.Keys) 从现有哈希表键创建一个数组; @(...) 确保将密钥集合复制到 静态数组 ,因为直接在修改相同哈希表的循环中使用 .Keys 属性 会休息。

循环体保存输入键的值,然后删除其旧名称下的条目。[1]

然后使用所需的名称转换在其新键名下重新创建该条目:

$key -creplace '(?<!^)\p{Lu} 匹配给定键中的每个大写字母 (\p{Lu}),字符串开头除外 ((?<!^)),并将其替换为 _后跟该字母 (_$&);将结果转换为大写 (.ToUpper()) 生成所需的名称。


[1] 在 之前删除旧条目 添加重命名的条目可以避免 单字 名称的问题,例如作为 Simplest,其转换后的名称 SIMPLEST 被认为是 相同的 名称,因为 PowerShell 中的哈希表不区分大小写。因此,在条目 Simplest 仍然存在时为条目 SIMPLEST 赋值实际上是针对 existing 条目,随后的 $test.Remove($key) 将简单地删除该条目条目,无需添加新条目。
指出问题的JosefZ点赞

($test.PsObject.Properties|Where-Object {$_.Name -eq "Keys"}).IsSettable 表示 False。因此,您需要分两步完成:

$test = @{
          ThisIsTheFirstColumn = "ValueInFirstColumn"; 
          ThisIsTheSecondColumn = "ValueInSecondColumn"
         }
$auxarr = $test.PsObject.Properties |
  Where-Object { $_.Name -eq "Keys" } | 
    select -ExpandProperty value 
$auxarr | ForEach-Object { 
    $aux = ($_.substring(0,1).toupper() + 
    $_.substring(1) -creplace '[^\p{Ll}\s]', '_$&').Trim("_").ToUpper()
    $test.ADD( $aux, $test.$_)
    $test.Remove( $_)
}
$test

两步法是必要的,因为尝试在唯一的管道中执行 REMOVEADD 方法会导致以下错误:

select : Collection was modified; enumeration operation may not execute.

编辑。不幸的是,上述解决方案在单字 Pascal Case 键的情况下会失败,例如Simplest = "ValueInSimplest"。这是改进后的脚本:

$test = @{
          ThisIsTheFirstColumn = "ValueInFirstColumn"; 
          ThisIsTheSecondColumn = "ValueInSecondColumn"
          Simplest = "ValueInSimplest" # the simplest (one word) PascalCase
         }
$auxarr = $test.PsObject.Properties |
  Where-Object { $_.Name -eq "Keys" } | 
    select -ExpandProperty value
$auxarr | ForEach-Object { 
    $aux = ($_.substring(0,1).toupper() + 
      $_.substring(1) -creplace '[^\p{Ll}\s]', '_$&').Trim("_").ToUpper()
    $newvalue =  $test.$_
    $test.Remove( $_)
    $test.Add( $aux, $newvalue)
}
$test