不确定Get-ChildItem结果格式

时间:2019-01-16 00:06:32

标签: powershell registry

我正在尝试编写一个脚本,该脚本将遍历每个注册表项并检查其权限。我创建了一个满足所有要求的测试注册表项,并且应该将其打印到控制台,但是每当我运行此脚本时,我总是会得到很多空白行。我相信这是我尝试遍历注册表项及其返回格式的方式。

$ErrorActionPreference = "SilentlyContinue"

Get-ChildItem -LiteralPath "HKLM:\" -Recurse | % {
  $output = Get-Acl $_.Name | format-list | Out-String
  $output = $output -split "`r`n"

  ForEach ($line in $output) {
    If ($line -contains "         BUILTIN\Users Allow  FullControl") {
      echo $_.Name
    }
  }
}

下面的脚本回显代码的“成功”部分:

$output = Get-Acl "HKLM:\SOFTWARE\testkey" | format-list | Out-String
$output = $output -split "`r`n"

ForEach ($line in $output) {
  If ($line -contains "         BUILTIN\Users Allow  FullControl") }
    echo "success"
  }
}

我希望第一个脚本将遍历所有HKLM密钥并返回“ testkey”,但它将向控制台回显大量空行。

1 个答案:

答案 0 :(得分:2)

我的代码仍然存在很多问题,但是首先我要弄清楚为什么您会看到空白行。

当您使用$output = Get-Acl $_.Name时,请花一些时间来了解$_的含义。这是ForEach-Item正在处理的“当前项”(您使用的别名是%),在这种情况下,这是从Get-ChildItem返回的注册表项。

此对象包含您要引用的属性.Name,但是如果查看该属性值,则会看到它是一个注册表项名称,因为regedit倾向于引用它,即{ {1}},这不是在PowerShell中调用它的方式。

然后,当您致电HKEY_LOCAL_MACHINE\Key时,Get-Acl $_.Name并不知道您要寻找什么。注意在“成功”示例中,如何使用Get-Acl给出了注册表项的完全限定路径;这是一个PSDrive,它引用PSProvider来类似于文件系统浏览注册表,并且其全名是HKLM:\,从而使条目的完整路径为: Microsoft.PowerShell.Core\Registry。 uck!

顺便说一下,您没有看到此错误的原因是因为您将Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\Key设置为$ErrorActionPreference,所以错误被忽略并且没有发送到屏幕。通常,您不应设置该设置。

不过,与其尝试解析和/或连接字符串,不如查看注册表项对象的全部属性,并查看是否可以使用某些东西:

SilentlyContinue

就像哈!有一个名为Get-ChildItem HKLM:\ -Recurse | Select -First 1 | Format-List * 的属性,看起来像具有完全限定的路径名​​!

PSPath

成功! (有关详情,请参见下文)


所以现在我要绕道而行,谈谈其他一些事情。我知道,这很漫长,但是请忍受我。

首先,是您使用Get-ChildItem HKLM:\ -Recurse | Select -First 1 | % { Get-Acl $_.PSPath } 。开始使用PowerShell时,通常会查看命令的 output 并查看其 format ,这与您使用其他shell一样,很常见,以便弄清楚如何解析或处理或以其他方式处理给定的信息。

这几乎总是一个错误。 PowerShell的主要功能之一是大量使用在命令之间传递的完整对象。这意味着具有属性和方法的完整.Net类。

认识到您在屏幕上看到的只是底层对象的格式化渲染非常重要。它可能不会显示所有内容,它可能会被截断,最重要的是,它不需要解析为文本

回到您的用法,使用任何| Format-List | Out-String命令时都要记住这一点:它旨在更改屏幕上对象显示格式 。它不可逆地丢失原始对象的数据,并且实际上只能用于显示目的,通常是用户在控制台上而不是脚本中。

如果查看Format-的输出,可能会认为将其解析为文本最容易,但是请记住,它是一个对象。相反,请看一下这种方法。您可以从任何目录中的PowerShell在PowerShell中运行它:

Get-Acl

(注意与以前的区别,我将其保留在管道中,使用$acl = Get-ChildItem HKLM:\ -Recurse | Select-Object -First 1 -ExpandProperty PSPath | Get-Acl 仅给我一个属性,然后将其通过管道直接传递到Select-Object中,而无需使用{{1}进行迭代})

现在看Get-Acl。您可能会以表格格式看到它,将其截断,因此可以使用ForEach-Object再次查看它,并查看它包含的更多属性。使用此输出指导编码。您的脚本正在“访问”列表中寻找条目,因此请尝试以下操作:

$acl

现在,您会看到一个新对象列表,每个对象都有自己的属性。这些是访问规则,并且包含与您需要的属性相对应的各个属性。它是它们的数组,因此您可以使用名为$acl | Format-List的命令对它们进行过滤(您可能已经看到了其别名$acl.Access Where-Object),但首先要检查这些对象:< / p>

Where

看起来?是它自己的类型$acl.Access[1].IdentityReference # ^ looks good for getting the identity you want to compare against $acl.Access[1].RegistryRights # ^ this one's tricky, looks like a string, but it's not # check its type and other members $acl.Access[1].RegistryRights | Get-Member 的对象,但是它似乎没有有用的属性。您可能还会注意到此属性的一些值看起来像逗号分隔的字符串。

这是一个枚举(枚举),基本上只是一堆带有名称的数字值,因此它们在代码中更有意义。这种特殊类型是位域,可以一次设置多个值。

在控制台中查看类型的快速提示,对枚举非常有用:类型名称(类)在PowerShell中用方括号.RegistryRights表示,例如,System.Security.AccessControl.RegistryRights或完全限定[]。您可以使用[string]而不是[System.String]访问类型本身的成员。您可以轻松地制表完整的类型,因此开始键入:::然后按 TAB ,现在键入.并再次按 TAB 。您可以开始循环浏览这些值。

由于该类型可以有多个值,因此可以使用由[RegistryRigh枚举提供的便捷方法(您可能在::的输出中看到)来确定它是否包含特定值。值。

HasFlag

我要掩饰一下,但是Get-Member是一回事:一个枚举。

现在,您可以将其与身份一起找到所需的内容:

$acl.Access[1].RegistryRights.HasFlag([System.Security.AccessControl.RegistryRights]::FullControl)

.Access.AccessControlType现在包含与您的条件相匹配的每个条目(应为1或0个条目,但大于1则无关紧要)。


我们将其与原始代码放在一起:

$UsersFullControlEntries = $acl.Access | Where-Object -FilterScript {
    $_.IdentityReference -eq 'BUILTIN\Users' -and 
    $_.RegistryRights.HasFlag([System.Security.AccessControl.RegistryRights]::FullControl) -and
   $_.AccessControlType.HasFlag([System.Security.AccessControl.AccessControlType]::Allow)
}

但是我们可以在这里做更多的事情。您的整个迭代过程都是寻找符合特定条件的项目,正如我们前面所看到的,$UsersFullControlEntries就是这么做的!它的脚本块应返回布尔值$desiredRights = [System.Security.AccessControl.RegistryRights]::FullControl $desiredControlType = [System.Security.AccessControl.AccessControlType]::Allow # this is helping formatting Get-ChildItem -LiteralPath "HKLM:" -Recurse | % { $acl = $_.PSPath | Get-Acl $UsersFullControlEntries = $acl.Access | Where-Object -FilterScript { $_.IdentityReference -eq 'BUILTIN\Users' -and $_.RegistryRights.HasFlag($desiredRights) -and $_.AccessControlType.HasFlag($desiredControlType) } if ($UsersFullControlEntries) { echo $_.Name } } / Where-Object,而不是返回输出,如果该值为$true,则为原始对象({{ 1}})将沿管道传递。因此,我们可以将$false替换为$true,以获得更好的语法:

$_
现在不需要

ForEach-Object,该值直接从脚本块中返回,直接解释为true / false,而无需编写条件,现在将原始对象返回给调用者(不是Where-Object属性),但这更好;您应该努力保留它,以便可以尽可能长时间地使用最完整的对象。在任何时候,您都可以引用或扩展返回对象的$desiredRights = [System.Security.AccessControl.RegistryRights]::FullControl $desiredControlType = [System.Security.AccessControl.AccessControlType]::Allow # this is helping formatting Get-ChildItem -LiteralPath "HKLM:" -Recurse | Where-Object { $acl = $_.PSPath | Get-Acl $acl.Access | Where-Object -FilterScript { $_.IdentityReference -eq 'BUILTIN\Users' -and $_.RegistryRights.HasFlag($desiredRights) -and $_.AccessControlType.HasFlag($desiredControlType) } } 属性,但是如果需要,您还可以使用所有其他属性。

再进行一次优化,现在让我们摆脱该$UsersFullControlEntries变量,而使其成为.Name内部的一个管道:

.Name

希望这为您提供了一些见识,想法和工具,以继续探索PowerShell!