powershell to resolve junction target path
Asked Answered
V

8

44

In PowerShell, I need resolve the target path of a junction (symlink).

for example, say I have a junction c:\someJunction whose target is c:\temp\target

I tried variations of $junc = Get-Item c:\someJunction, but was only able to get c:\someJunction

How do I find the target path of the junction, in this example c:\temp\target, of a given junction?

Vaal answered 4/6, 2013 at 19:40 Comment(1)
Answer by Jordan Shurmer below: $junc = (Get-Item c:\someJunction).TargetWirra
B
9

You can get the path by doing the following:

Get-ChildItem -Path C:\someJunction

Edit for finding the path and not the contents of the folder

Add-Type -MemberDefinition @"
private const int FILE_SHARE_READ = 1;
private const int FILE_SHARE_WRITE = 2;

private const int CREATION_DISPOSITION_OPEN_EXISTING = 3;
private const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;

[DllImport("kernel32.dll", EntryPoint = "GetFinalPathNameByHandleW", CharSet = CharSet.Unicode, SetLastError = true)]
 public static extern int GetFinalPathNameByHandle(IntPtr handle, [In, Out] StringBuilder path, int bufLen, int flags);

[DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode, SetLastError = true)]
 public static extern SafeFileHandle CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode,
 IntPtr SecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);

 public static string GetSymbolicLinkTarget(System.IO.DirectoryInfo symlink)
 {
     SafeFileHandle directoryHandle = CreateFile(symlink.FullName, 0, 2, System.IntPtr.Zero, CREATION_DISPOSITION_OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, System.IntPtr.Zero);
     if(directoryHandle.IsInvalid)
     throw new Win32Exception(Marshal.GetLastWin32Error());

     StringBuilder path = new StringBuilder(512);
     int size = GetFinalPathNameByHandle(directoryHandle.DangerousGetHandle(), path, path.Capacity, 0);
     if (size<0)
     throw new Win32Exception(Marshal.GetLastWin32Error());
     // The remarks section of GetFinalPathNameByHandle mentions the return being prefixed with "\\?\"
     // More information about "\\?\" here -> http://msdn.microsoft.com/en-us/library/aa365247(v=VS.85).aspx
     if (path[0] == '\\' && path[1] == '\\' && path[2] == '?' && path[3] == '\\')
     return path.ToString().Substring(4);
     else
     return path.ToString();
 }
"@ -Name Win32 -NameSpace System -UsingNamespace System.Text,Microsoft.Win32.SafeHandles,System.ComponentModel

$dir = Get-Item D:\1
[System.Win32]::GetSymbolicLinkTarget($dir)
Borg answered 4/6, 2013 at 19:46 Comment(6)
This looks like the same thing.Boudoir
@BobLobLaw Actually got it from chrisbensen.blogspot.ru/2010/06/getfinalpathnamebyhandle.htmlBorg
Thanks, it worked! I feel that I must complain about the effort required to get something as simple as a junction's target dir, but that's not your fault... my beef's with powershell.Vaal
@Vaal There's no need for anything complicated here – https://mcmap.net/q/370607/-powershell-to-resolve-junction-target-pathPucida
Special reminder ⚠ : it seems that there is no need for such trouble now, because PowerShell V5 has provided good support for various reparse points. If you have any questions about this problem, you can refer to the latest answers and the latest official documents, which have a more detailed explanation.Rill
I was still having issues with using the native functions, especially for folders like C:\Documents and Settings which threw either access denied or other similar errors. In the end I used this code, but changed the GetSymbolicLinkTarget to accept a simple string and it appears to be working extremely wellSissy
T
49

New-Item, Remove-Item, and Get-ChildItem have been enhanced to support creating and managing symbolic links. The -ItemType parameter for New-Item accepts a new value, SymbolicLink. Now you can create symbolic links in a single line by running the New-Item cmdlet.

What's New in Windows PowerShell v5

I've checked the symlink support on the my Windows 7 machine, it's works fine.

PS> New-Item -Type SymbolicLink -Target C:\ -Name TestSymlink


    Directory: C:\Users\skokhanovskiy\Desktop


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d----l       06.09.2016     18:27                TestSymlink

Get target of the symbolic link as easy as to create it.

> Get-Item .\TestSymlink | Select-Object -ExpandProperty Target
C:\
Teacher answered 6/9, 2016 at 13:41 Comment(6)
For those reading this answer beware that this answer does not work for symlink\some\path but Josh's accepted answer does.Mott
@AndrewSavinykh full paths work fine for me. Shorthand -> (Get-Item C:\somejunction).Target -> C:\temp\target\Hagiolatry
Note: Still as of PowerShell v7.0.0-preview.4, there is a permissions-related bug with respect to the hidden system-defined junctions (e.g., $HOME\Cookies) - see this GitHub issue.Torpid
@Brain2000: I think what Andrew means is that this answer works with paths that refer to junctions / symlinks themselves, but not with paths based on such symlinks; to put it in terms of this answer: (Get-Item .\TestSymlink).Target works, but (Get-Item .\TestSymlink\Windows).Target does not.Torpid
I need the target of a .lnk file. In Explorer, I get it with context menu, properties, target field.Tracy
It's easier when you don't allow PowerShell to hide information from you when using default format-table output. The following will show things like target, mode and various timestamps: get-item -Path "...." | format-listMalvia
W
26

There are some really complicated answers to this question! Here's a super simple and self explanatory one:

(Get-Item C:\somejunction).Target
Withstand answered 28/2, 2020 at 13:41 Comment(1)
The prior answers are for Powershell 4 or older, which improved reparse point support. PS 4 doesn't support the LinkType or Target attributes. Frustrating.Waddle
B
9

You can get the path by doing the following:

Get-ChildItem -Path C:\someJunction

Edit for finding the path and not the contents of the folder

Add-Type -MemberDefinition @"
private const int FILE_SHARE_READ = 1;
private const int FILE_SHARE_WRITE = 2;

private const int CREATION_DISPOSITION_OPEN_EXISTING = 3;
private const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;

[DllImport("kernel32.dll", EntryPoint = "GetFinalPathNameByHandleW", CharSet = CharSet.Unicode, SetLastError = true)]
 public static extern int GetFinalPathNameByHandle(IntPtr handle, [In, Out] StringBuilder path, int bufLen, int flags);

[DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode, SetLastError = true)]
 public static extern SafeFileHandle CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode,
 IntPtr SecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);

 public static string GetSymbolicLinkTarget(System.IO.DirectoryInfo symlink)
 {
     SafeFileHandle directoryHandle = CreateFile(symlink.FullName, 0, 2, System.IntPtr.Zero, CREATION_DISPOSITION_OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, System.IntPtr.Zero);
     if(directoryHandle.IsInvalid)
     throw new Win32Exception(Marshal.GetLastWin32Error());

     StringBuilder path = new StringBuilder(512);
     int size = GetFinalPathNameByHandle(directoryHandle.DangerousGetHandle(), path, path.Capacity, 0);
     if (size<0)
     throw new Win32Exception(Marshal.GetLastWin32Error());
     // The remarks section of GetFinalPathNameByHandle mentions the return being prefixed with "\\?\"
     // More information about "\\?\" here -> http://msdn.microsoft.com/en-us/library/aa365247(v=VS.85).aspx
     if (path[0] == '\\' && path[1] == '\\' && path[2] == '?' && path[3] == '\\')
     return path.ToString().Substring(4);
     else
     return path.ToString();
 }
"@ -Name Win32 -NameSpace System -UsingNamespace System.Text,Microsoft.Win32.SafeHandles,System.ComponentModel

$dir = Get-Item D:\1
[System.Win32]::GetSymbolicLinkTarget($dir)
Borg answered 4/6, 2013 at 19:46 Comment(6)
This looks like the same thing.Boudoir
@BobLobLaw Actually got it from chrisbensen.blogspot.ru/2010/06/getfinalpathnamebyhandle.htmlBorg
Thanks, it worked! I feel that I must complain about the effort required to get something as simple as a junction's target dir, but that's not your fault... my beef's with powershell.Vaal
@Vaal There's no need for anything complicated here – https://mcmap.net/q/370607/-powershell-to-resolve-junction-target-pathPucida
Special reminder ⚠ : it seems that there is no need for such trouble now, because PowerShell V5 has provided good support for various reparse points. If you have any questions about this problem, you can refer to the latest answers and the latest official documents, which have a more detailed explanation.Rill
I was still having issues with using the native functions, especially for folders like C:\Documents and Settings which threw either access denied or other similar errors. In the end I used this code, but changed the GetSymbolicLinkTarget to accept a simple string and it appears to be working extremely wellSissy
A
7

This does the trick with less work, and works even for junctions on a remote server:

fsutil reparsepoint query "M:\Junc"

If you want just the target name:

fsutil reparsepoint query "M:\Junc" | where-object { $_ -imatch 'Print Name:' } | foreach-object { $_ -replace 'Print Name\:\s*','' }

so

function Get_JunctionTarget($p_path)
{
    fsutil reparsepoint query $p_path | where-object { $_ -imatch 'Print Name:' } | foreach-object { $_ -replace 'Print Name\:\s*','' }
}

Also, the code below is a slight modification of the code that Josh provided above. It can be put in a file that is read multiple times, and it handles the leading \\?\ correctly in the case of a network drive:

function Global:Get_UNCPath($l_dir)
{
    if( ( ([System.Management.Automation.PSTypeName]'System.Win32').Type -eq $null)  -or ([system.win32].getmethod('GetSymbolicLinkTarget') -eq $null) )
    {
        Add-Type -MemberDefinition @"
private const int CREATION_DISPOSITION_OPEN_EXISTING = 3;
private const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;

[DllImport("kernel32.dll", EntryPoint = "GetFinalPathNameByHandleW", CharSet = CharSet.Unicode, SetLastError = true)]
 public static extern int GetFinalPathNameByHandle(IntPtr handle, [In, Out] StringBuilder path, int bufLen, int flags);

[DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode, SetLastError = true)]
 public static extern SafeFileHandle CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode,
 IntPtr SecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);

 public static string GetSymbolicLinkTarget(System.IO.DirectoryInfo symlink)
 {
     SafeFileHandle directoryHandle = CreateFile(symlink.FullName, 0, 2, System.IntPtr.Zero, CREATION_DISPOSITION_OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, System.IntPtr.Zero);
     if(directoryHandle.IsInvalid)
     {
         throw new Win32Exception(Marshal.GetLastWin32Error());
     }
     StringBuilder path = new StringBuilder(512);
     int size = GetFinalPathNameByHandle(directoryHandle.DangerousGetHandle(), path, path.Capacity, 0);
     if (size<0)
     {
         throw new Win32Exception(Marshal.GetLastWin32Error());
     }
     // The remarks section of GetFinalPathNameByHandle mentions the return being prefixed with "\\?\"
     // More information about "\\?\" here -> http://msdn.microsoft.com/en-us/library/aa365247(v=VS.85).aspx
     string sPath = path.ToString();
     if( sPath.Length>8 && sPath.Substring(0,8) == @"\\?\UNC\" )
     {
         return @"\" + sPath.Substring(7);
     }
     else if( sPath.Length>4 && sPath.Substring(0,4) == @"\\?\" )
     {
         return sPath.Substring(4);
     }
     else
     {
         return sPath;
     }
 }
"@ -Name Win32 -NameSpace System -UsingNamespace System.Text,Microsoft.Win32.SafeHandles,System.ComponentModel
    }
    [System.Win32]::GetSymbolicLinkTarget($l_dir)
}

and given the function Get_UNCPath above, we can improve the function Get_JunctionTarget as follows:

function Global:Get_JunctionTarget([string]$p_path)
{
    $l_target = fsutil reparsepoint query $p_path | where-object { $_ -imatch 'Print Name\:' } | foreach-object { $_ -replace 'Print Name\:\s*','' }
    if( $l_target -imatch "(^[A-Z])\:\\" )
    {
        $l_drive = $matches[1]
        $l_uncPath = Get_UncPath $p_path
        if( $l_uncPath -imatch "(^\\\\[^\\]*\\)" )
        {
            $l_machine = $matches[1]
            $l_target = $l_target -replace "^$l_drive\:","$l_machine$l_drive$"
        }
    }
    $l_target
}
Anderegg answered 6/9, 2014 at 22:45 Comment(1)
Thanks...the first command worked for me really well. It just took a moment to realize the output included the hex bytes of the path...as well as the ascii path (on the right).Jerrine
M
2

We end up using this function

function Get-SymlinkTargetDirectory {           
    [cmdletbinding()]
    param(
        [string]$SymlinkDir
    )
    $basePath = Split-Path $SymlinkDir
    $folder = Split-Path -leaf $SymlinkDir
    $dir = cmd /c dir /a:l $basePath | Select-String $folder
    $dir = $dir -join ' '
    $regx = $folder + '\ *\[(.*?)\]'
    $Matches = $null
    $found = $dir -match $regx
    if ($found) {
        if ($Matches[1]) {
            Return $Matches[1]
        }
    }
    Return '' 
}
Mortgage answered 4/5, 2016 at 0:32 Comment(2)
Only solution for system reparse points like 'C:\Users\Joe\SendTo' afaictCloudy
@Frank: Josh' answer can do that too, more robustly, though you pay a performance penalty on first invocation for the on-demand compilation; subsequent calls will be faster than this approach, though. In PSv5+, (Get-Item -Force $HOME\SendTo).Target should work, but currently (as of v7.0.0-preview.4) doesn't, due to a bug - with regular symlinks (reparse points), it works fine, however.Torpid
U
2

At least in PSv5 it's as easy as this to list all targets of some dirs links (or further down a single one) and get it as objects and nicely formatted (e.g. all the *~ dirs are actually junctions):

C:\Jaspersoft> ls | select name, target

Name                          Target
----                          ------
apache-websrv~                {C:\Program Files (x86)\Apache24\}
jasperreports-server-cp-6.3.0 {}
jasperreports-server-cp~      {C:\Jaspersoft\jasperreports-server-cp-6.3.0}
jr-srv-cp~                    {C:\Jaspersoft\jasperreports-server-cp~}

for one link:

C:\Jaspersoft> ls . apache-websrv~ | select name, target

Name           Target
----           ------
apache-websrv~ {C:\Program Files (x86)\Apache24\}

or (to just get the Target as a String value for the C:\Jaspersoft\apache-websrv~ junction):

> ls  C:\Jaspersoft  apache-websrv~  | %{$_.target}
C:\Program Files (x86)\Apache24\

The standard ls would look like this for the examples:

C:\Jaspersoft> ls

    Verzeichnis: C:\Jaspersoft


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d----l       01.04.2019     15:05                apache-websrv~
d-----       02.04.2019     10:30                jasperreports-server-cp-6.3.0
d----l       05.10.2018     15:19                jasperreports-server-cp~
d----l       12.02.2019     11:46                jr-srv-cp~

(Other answers contained this in a way as well but not easily visible/understandable)

Unwished answered 2/4, 2019 at 18:49 Comment(0)
R
2

It seems that with PS5 (as mentioned here or somewhere else?), system-defined junctions/reparse points have a bug whereas symlinks and user-defined junctions/reparse points do not. These solutions seem to work for symlinks but not junctions. The only thing that I could get to work to show the proper info was the fsutil program. The only issue is that it outputs data similar to Format-Hex as shown below.

The fsutil tool give me this output when used with the Windows Store app stored in $path

PS C:\> $path = "$($env:USERPROFILE)\AppData\Local\Microsoft\WindowsApps\Skype.exe"
PS C:\> fsutil reparsepoint query $path
Reparse Data Length: 0x150
Reparse Data:
0000:  03 00 00 00 4d 00 69 00  63 00 72 00 6f 00 73 00  ....M.i.c.r.o.s.
0010:  6f 00 66 00 74 00 2e 00  53 00 6b 00 79 00 70 00  o.f.t...S.k.y.p.
0020:  65 00 41 00 70 00 70 00  5f 00 6b 00 7a 00 66 00  e.A.p.p._.k.z.f.
0030:  38 00 71 00 78 00 66 00  33 00 38 00 7a 00 67 00  8.q.x.f.3.8.z.g.
0040:  35 00 63 00 00 00 4d 00  69 00 63 00 72 00 6f 00  5.c...M.i.c.r.o.
0050:  73 00 6f 00 66 00 74 00  2e 00 53 00 6b 00 79 00  s.o.f.t...S.k.y.
0060:  70 00 65 00 41 00 70 00  70 00 5f 00 6b 00 7a 00  p.e.A.p.p._.k.z.
0070:  66 00 38 00 71 00 78 00  66 00 33 00 38 00 7a 00  f.8.q.x.f.3.8.z.
0080:  67 00 35 00 63 00 21 00  41 00 70 00 70 00 00 00  g.5.c.!.A.p.p...
0090:  43 00 3a 00 5c 00 50 00  72 00 6f 00 67 00 72 00  C.:.\.P.r.o.g.r.
00a0:  61 00 6d 00 20 00 46 00  69 00 6c 00 65 00 73 00  a.m. .F.i.l.e.s.
00b0:  5c 00 57 00 69 00 6e 00  64 00 6f 00 77 00 73 00  \.W.i.n.d.o.w.s.
00c0:  41 00 70 00 70 00 73 00  5c 00 4d 00 69 00 63 00  A.p.p.s.\.M.i.c.
00d0:  72 00 6f 00 73 00 6f 00  66 00 74 00 2e 00 53 00  r.o.s.o.f.t...S.
00e0:  6b 00 79 00 70 00 65 00  41 00 70 00 70 00 5f 00  k.y.p.e.A.p.p._.
00f0:  31 00 35 00 2e 00 36 00  34 00 2e 00 38 00 30 00  1.5...6.4...8.0.
0100:  2e 00 30 00 5f 00 78 00  38 00 36 00 5f 00 5f 00  ..0._.x.8.6._._.
0110:  6b 00 7a 00 66 00 38 00  71 00 78 00 66 00 33 00  k.z.f.8.q.x.f.3.
0120:  38 00 7a 00 67 00 35 00  63 00 5c 00 53 00 6b 00  8.z.g.5.c.\.S.k.
0130:  79 00 70 00 65 00 5c 00  53 00 6b 00 79 00 70 00  y.p.e.\.S.k.y.p.
0140:  65 00 2e 00 65 00 78 00  65 00 00 00 30 00 00 00  e...e.x.e...0...

OR

PS C:\> $path2 = "$HOME/My Documents"
PS C:\> fsutil reparsepoint query $path2
Reparse Tag Value : 0xa0000003
Tag value: Microsoft
Tag value: Name Surrogate
Tag value: Mount Point
Substitue Name offset: 0
Substitue Name length: 56
Print Name offset:     58
Print Name Length:     48
Substitute Name:       \??\C:\Users\chefh\Documents
Print Name:            C:\Users\chefh\Documents

Reparse Data Length: 0x74      
Reparse Data:
0000:  00 00 38 00 3a 00 30 00  5c 00 3f 00 3f 00 5c 00  ..8.:.0.\.?.?.\.
0010:  43 00 3a 00 5c 00 55 00  73 00 65 00 72 00 73 00  C.:.\.U.s.e.r.s.
0020:  5c 00 63 00 68 00 65 00  66 00 68 00 5c 00 44 00  \.c.h.e.f.h.\.D.
0030:  6f 00 63 00 75 00 6d 00  65 00 6e 00 74 00 73 00  o.c.u.m.e.n.t.s.
0040:  00 00 43 00 3a 00 5c 00  55 00 73 00 65 00 72 00  ..C.:.\.U.s.e.r.
0050:  73 00 5c 00 63 00 68 00  65 00 66 00 68 00 5c 00  s.\.c.h.e.f.h.\.
0060:  44 00 6f 00 63 00 75 00  6d 00 65 00 6e 00 74 00  D.o.c.u.m.e.n.t.
0070:  73 00 00 00                                       s...

Here is the code that I came up with to parse that output. I know that it is quite messy code, but as I said I couldn't get the other solutions here to work for Windows Stores apps. This one does. Please test it in your uses and mention if there are issues.

***** Edited to allow files or directories. *****

$path = "$($env:USERPROFILE)\AppData\Local\Microsoft\WindowsApps\Skype.exe"
$path2 = "$HOME/My Documents"

function Get-ReparseTarget ($path)
{
    #   Grabs output of fsutil
    $a = fsutil reparsepoint query $path

    #   Regex to capture fsutil output
    $regex = '[0-9a-fA-F]+\:\s\s(?<chunk1>([0-9a-fA-F]{2}\s){1,8}\s)(?<chunk2>([0-9a-fA-F]{2}\s){1,8}\s){0,1}.+'

    #   Splits and trims the "chunks" then adds them together to create an array of hex character
    $c = $a.foreach({if ($_ -match $regex) {($Matches['chunk1'] -split ' ').trim() | `
        where {$_ -ne ""};($Matches['chunk2'] -split ' ').trim() |where {$_ -ne ""}}})

    #   Convert an Array of Hex(String) to Array of Bytes
    $f = [byte[]]($c | foreach{[Convert]::ToInt32($_,16)})

    #   Convert the Unicode to Ascii and convert '' (00 in hex) to spaces
    $g = [System.Text.Encoding]::Unicode.GetChars($f).foreach({if([int]$_ -eq 0) {' '} else {$_}})

    #   Combine Char[] to String then Split into the important bits and Select the Reparse Target Path 
    #   depending on whether the Path argument is a file or directory
    switch ((get-item -Force -Path $path))
    {
        {$_ -is [System.IO.FileInfo]} {$h = ($g[0..($g.Count -4)] -join "" -split " ", 4)[3]}
        {$_ -is [System.IO.DirectoryInfo]} {$h = ($g[0..($g.Count -2)] -join "" -split " ", 3)[2]}
        Default { Write-Error "Path must be either a file or directory"}
    }

    #   Return the path
    return $h
}

#  Quick Test
Get-ReparseTarget -path $path
Get-ReparseTarget -path $path2


# Input:
#   $path = "$($env:USERPROFILE)\AppData\Local\Microsoft\WindowsApps\Skype.exe"
# Run:
#   Get-ReparseTarget $path
# Returns:
#   C:\Program Files\WindowsApps\Microsoft.SkypeApp_15.64.80.0_x86__kzf8qxf38zg5c\Skype\Skype.exe

# Input:
#   $path2 = "$HOME/My Documents"
# Run:
#   Get-ReparseTarget $path2
# Returns:
#   C:\Users\chefh\Documents
Rhenium answered 14/10, 2020 at 20:31 Comment(0)
H
-1

Based on the answer of The Nerdy Chef I simplified the code a bit. Advantage: That way it works on junction too.

function Get-ReparseTarget {
    [cmdletbinding()]
    param(
        [Parameter(Mandatory=$true)][string]$path
    )
    $fsutil = fsutil.exe reparsepoint query $path
    # gets the hex-stream out of fsutil output as array
    $hex = ($fsutil.Where({$_ -match "[0-9a-f]{4}: .*"}) | Select-String "[0-9a-f][0-9a-f] " -AllMatches).Matches.Value.Trim()
    # Convert to Bytestream
    $Bytestream = [byte[]]($hex | foreach{[Convert]::ToInt32($_,16)})
    # Unicode2Ascii + Trim the "Trailing Zero", which is added depending on the target type.
    $Unicode = ([System.Text.Encoding]::Unicode.GetChars($Bytestream) -join '').TrimEnd("`0")
    # We split by "Zero Character" and by "\??\", and keep the latest match, works for a file, a directory and a junction.
    $($Unicode -split "`0" -split "\\\?\?\\")[-1]
}
Homager answered 31/5, 2021 at 22:15 Comment(1)
I condensed this further into a single-line (with some further optimizations) with ([System.Text.Encoding]::Unicode.GetChars((fsutil.exe reparsepoint query $path | Select-String "[0-9a-f]{4}:\s\s(?:([0-9a-f]{2})\s*)+" -AllMatches | ForEach-Object {$_.Matches.Groups[1].Captures | ForEach-Object {[Convert]::ToInt32($_.Value,16)}})) -join '' -split "`0").TrimEnd("0") | Where-Object {$_} | Select-Object -Last 1. You can add back in the -split "\\\?\?\\" or maybe just change it to -replace if you like.Evincive

© 2022 - 2024 — McMap. All rights reserved.