PowerShell에서 마지막 쓰기 시간 / 마지막 수정 시간별로 레지스트리 항목을 정렬하는 방법

Dec 14 2020

PowerShell을 배우고 있으며 Windows 10 20H2에서 PSCore7.1을 사용합니다. 현재 다음과 같이 할 수 있습니다.

Get-ChildItem -Path "C:\" -Directory -Recurse -Depth 5 | Where-Object{$_.LastWriteTime.ToString("yyyy-MM-dd") -eq "2020-12-14"} | Sort-Object LastWriteTime

지정된 디렉토리 (이 경우 C :)의 지정된 깊이 (이 경우 5) 내에서 하위 폴더를 재귀 적으로 가져온 다음 지정된 시간 범위 (이 경우 오늘 또는 2020 년 12 월 14 일)에서 수정 된 개체를 찾은 다음 마지막으로 정렬합니다. 타임 스탬프 별 결과.

하지만 레지스트리를보기 위해 get-childitem을 실행하면 다음과 같습니다.

Get-ChildItem -Path "HKLM:\SOFTWARE"

이름과 속성, 타임 스탬프 없음, 그리고 regedit.exe에도 타임 스탬프가없는 두 항목 만 있습니다. 그러면 PowerShell을 사용하여 위에 게시 한 명령과 같이 마지막으로 수정 한 시간별로 레지스트리 키를 정렬하려면 어떻게해야합니까?

편집 : 여기 수퍼 유저에서 읽었습니다. 레지스트리 편집기에서 레지스트리 키를 txt 파일로 내 보내서 타임 스탬프를 볼 수 있지만 간단하지만 콘솔에서 타임 스탬프별로 레지스트리 키를 정렬하고 싶기 때문에이 질문과는 무관합니다.

재 편집 :

이 명령을 실행했습니다.

get-childitem -path "HKLM:\"  | Get-Member

결과는 다음과 같습니다.

   TypeName: Microsoft.Win32.RegistryKey

Name                      MemberType   Definition
----                      ----------   ----------
Close                     Method       void Close()
CreateSubKey              Method       Microsoft.Win32.RegistryKey CreateSubKey(string subkey), Microsoft.Win32.RegistryKey CreateSubKey(string subkey, bool writable…
DeleteSubKey              Method       void DeleteSubKey(string subkey), void DeleteSubKey(string subkey, bool throwOnMissingSubKey)
DeleteSubKeyTree          Method       void DeleteSubKeyTree(string subkey), void DeleteSubKeyTree(string subkey, bool throwOnMissingSubKey)
DeleteValue               Method       void DeleteValue(string name), void DeleteValue(string name, bool throwOnMissingValue)
Dispose                   Method       void Dispose(), void IDisposable.Dispose()
Equals                    Method       bool Equals(System.Object obj)
Flush                     Method       void Flush()
GetAccessControl          Method       System.Security.AccessControl.RegistrySecurity GetAccessControl(), System.Security.AccessControl.RegistrySecurity GetAccessCon…
GetHashCode               Method       int GetHashCode()
GetLifetimeService        Method       System.Object GetLifetimeService()
GetSubKeyNames            Method       string[] GetSubKeyNames()
GetType                   Method       type GetType()
GetValue                  Method       System.Object GetValue(string name), System.Object GetValue(string name, System.Object defaultValue), System.Object GetValue(s…
GetValueKind              Method       Microsoft.Win32.RegistryValueKind GetValueKind(string name)
GetValueNames             Method       string[] GetValueNames()
InitializeLifetimeService Method       System.Object InitializeLifetimeService()
OpenSubKey                Method       Microsoft.Win32.RegistryKey OpenSubKey(string name), Microsoft.Win32.RegistryKey OpenSubKey(string name, bool writable), Micro…
SetAccessControl          Method       void SetAccessControl(System.Security.AccessControl.RegistrySecurity registrySecurity)
SetValue                  Method       void SetValue(string name, System.Object value), void SetValue(string name, System.Object value, Microsoft.Win32.RegistryValue…
ToString                  Method       string ToString()
Property                  NoteProperty string[] Property=System.String[]
PSChildName               NoteProperty string PSChildName=BCD00000000
PSDrive                   NoteProperty PSDriveInfo PSDrive=HKLM
PSIsContainer             NoteProperty bool PSIsContainer=True
PSParentPath              NoteProperty string PSParentPath=Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE
PSPath                    NoteProperty string PSPath=Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\BCD00000000
PSProvider                NoteProperty ProviderInfo PSProvider=Microsoft.PowerShell.Core\Registry
Handle                    Property     Microsoft.Win32.SafeHandles.SafeRegistryHandle Handle {get;}
Name                      Property     string Name {get;}
SubKeyCount               Property     int SubKeyCount {get;}
ValueCount                Property     int ValueCount {get;}
View                      Property     Microsoft.Win32.RegistryView View {get;}

보시다시피 타임 스탬프가 없습니다 ...

인용 : Windows 레지스트리 키 / 값의 변경 날짜를 볼 수있는 방법이 있습니까?

증명:

Key Name:          HKEY_CLASSES_ROOT\._bsln140
Class Name:        <NO CLASS>
Last Write Time:   2020-12-11 - 14:54
Value 0
  Name:            <NO NAME>
  Type:            REG_SZ
  Data:            VisualStudio.Launcher._bsln140

답변

2 user19702 Dec 14 2020 at 16:21

불행히도 Windows가 레지스트리 키 정보에 액세스하는 방법으로 인해 복잡해질 수 있지만 가능합니다. 다음은 Microsoft devblog에서 Scripto 박사가 작성한 일련의 자습서입니다.

  • powershell을 사용하여 레지스트리에 액세스 마지막으로 수정 된 타임 스탬프 는 powershell에서 Win32 함수 RegQueryInfoKey를 사용하여 타임 스탬프를 얻는 방법을 설명합니다 .
  • powershell 레지스트리 타임 스탬프 코드 재사용에서는 해당 코드를 Windows PowerShell 함수로 래핑하여 재사용 가능한 도구를 만드는 방법을 다룹니다.
  • powershell을 통해 레지스트리 키 타임 스탬프를 활용하여 실제 사용을 다룹니다.

최종 제품은 다음과 같이 Add-RegKeyLastWriteTime.ps1로 저장됩니다.

#requires -version 3.0

function Add-RegKeyLastWriteTime {
[CmdletBinding()]
param(
    [Parameter(Mandatory, ParameterSetName="ByKey", Position=0, ValueFromPipeline)]
    # Registry key object returned from Get-ChildItem or Get-Item
    [Microsoft.Win32.RegistryKey] $RegistryKey, [Parameter(Mandatory, ParameterSetName="ByPath", Position=0)] # Path to a registry key [string] $Path
)

 begin {
    # Define the namespace (string array creates nested namespace):
    $Namespace = "HeyScriptingGuy" # Make sure type is loaded (this will only get loaded on first run): Add-Type @" using System; using System.Text; using System.Runtime.InteropServices; $($Namespace | ForEach-Object { "namespace $_ {"
        })
            public class advapi32 {
                [DllImport("advapi32.dll", CharSet = CharSet.Auto)]
                public static extern Int32 RegQueryInfoKey(
                    Microsoft.Win32.SafeHandles.SafeRegistryHandle hKey,
                    StringBuilder lpClass,
                    [In, Out] ref UInt32 lpcbClass,
                    UInt32 lpReserved,
                    out UInt32 lpcSubKeys,
                    out UInt32 lpcbMaxSubKeyLen,
                    out UInt32 lpcbMaxClassLen,
                    out UInt32 lpcValues,
                    out UInt32 lpcbMaxValueNameLen,
                    out UInt32 lpcbMaxValueLen,
                    out UInt32 lpcbSecurityDescriptor,
                    out System.Runtime.InteropServices.ComTypes.FILETIME lpftLastWriteTime
                );
            }
        $($Namespace | ForEach-Object { "}" })
"@
   
    # Get a shortcut to the type:   
    $RegTools = ("{0}.advapi32" -f ($Namespace -join ".")) -as [type]
}
 process {
    switch ($PSCmdlet.ParameterSetName) { "ByKey" { # Already have the key, no more work to be done 🙂 } "ByPath" { # We need a RegistryKey object (Get-Item should return that) $Item = Get-Item -Path $Path -ErrorAction Stop # Make sure this is of type [Microsoft.Win32.RegistryKey] if ($Item -isnot [Microsoft.Win32.RegistryKey]) {
                throw "'$Path' is not a path to a registry key!" } $RegistryKey = $Item } } # Initialize variables that will be populated: $ClassLength = 255 # Buffer size (class name is rarely used, and when it is, I've never seen
                        # it more than 8 characters. Buffer can be increased here, though.
    $ClassName = New-Object System.Text.StringBuilder $ClassLength  # Will hold the class name
    $LastWriteTime = New-Object System.Runtime.InteropServices.ComTypes.FILETIME switch ($RegTools::RegQueryInfoKey($RegistryKey.Handle, $ClassName,
        [ref] $ClassLength, $null,  # Reserved
        [ref] $null, # SubKeyCount [ref] $null, # MaxSubKeyNameLength
        [ref] $null, # MaxClassLength [ref] $null, # ValueCount
        [ref] $null, # MaxValueNameLength [ref] $null, # MaxValueValueLength
        [ref] $null, # SecurityDescriptorSize [ref] $LastWriteTime
    )) {
         0 { # Success
            # Convert to DateTime object:
            $UnsignedLow = [System.BitConverter]::ToUInt32([System.BitConverter]::GetBytes($LastWriteTime.dwLowDateTime), 0)
            $UnsignedHigh = [System.BitConverter]::ToUInt32([System.BitConverter]::GetBytes($LastWriteTime.dwHighDateTime), 0)
            # Shift high part so it is most significant 32 bits, then copy low part into 64-bit int:
            $FileTimeInt64 = ([Int64] $UnsignedHigh -shl 32) -bor $UnsignedLow # Create datetime object $LastWriteTime = [datetime]::FromFileTime($FileTimeInt64) # Add properties to object and output them to pipeline $RegistryKey | Add-Member -NotePropertyMembers @{
                LastWriteTime = $LastWriteTime ClassName = $ClassName.ToString()
            } -PassThru -Force
        }
        122  { # ERROR_INSUFFICIENT_BUFFER (0x7a)
            throw "Class name buffer too small"
            # function could be recalled with a larger buffer, but for
            # now, just exit
        }
        default {
            throw "Unknown error encountered (error code $_)"
        }
    }
}
}

마지막으로 사용 예 :

Get-ChildItem HKCU:\ | Add-RegKeyLastWriteTime | Select Name,LastWriteTime
'
Name                                                         LastWriteTime         
----                                                         -------------         
HKEY_CURRENT_USER\AppEvents                                  7/6/2020 8:56:11 AM   
HKEY_CURRENT_USER\Console                                    7/6/2020 8:56:11 AM   
HKEY_CURRENT_USER\Control Panel                              7/6/2020 1:04:53 PM  
'