35

PowerShell では、ジャンクション (シンボリック リンク) のターゲット パスを解決する必要があります。

たとえば、c:\someJunctionターゲットがc:\temp\target

のバリエーションを試し$junc = Get-Item c:\someJunctionましたが、取得できたのはc:\someJunction

この例では、特定のジャンクションのジャンクションのターゲット パスを見つけるにはどうすればよいc:\temp\targetですか?

4

8 に答える 8

47

New-Item、Remove-Item、および Get-ChildItem が拡張され、シンボリック リンクの作成と管理がサポートされるようになりました。New-Item の -ItemType パラメーターは、新しい値 SymbolicLink を受け入れます。New-Item コマンドレットを実行して、1 行でシンボリック リンクを作成できるようになりました。

Windows PowerShell v5 の新機能

Windows 7 マシンでシンボリック リンクのサポートを確認しましたが、問題なく動作しています。

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-Item .\TestSymlink | Select-Object -ExpandProperty Target
C:\
于 2016-09-06T13:41:00.767 に答える
4

これはより少ない作業でトリックを実行し、リモート サーバー上のジャンクションに対しても機能します。

fsutil reparsepoint query "M:\Junc"

ターゲット名だけが必要な場合:

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

それで

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

また、以下のコードは、Josh が上で提供したコードを少し変更したものです。複数回読み取られるファイルに入れることができ\\?\、ネットワークドライブの場合は先頭を正しく処理します。

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)
}

上記の関数が与えられた場合、次のようGet_UNCPathに関数を改善できます。Get_JunctionTarget

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
}
于 2014-09-06T22:45:07.280 に答える
1

PS5 では (ここまたは他の場所で言及されているように)、システム定義のジャンクション/再解析ポイントにはバグがあるようですが、シンボリック リンクとユーザー定義のジャンクション/再解析ポイントにはバグがありません。これらのソリューションは、シンボリックリンクでは機能するようですが、ジャンクションでは機能しないようです。適切な情報を表示するために作業できたのは、fsutil プログラムだけでした。唯一の問題は、以下に示すように Format-Hex に似たデータを出力することです。

fsutil ツールを $path に保存されている Windows ストア アプリで使用すると、この出力が得られます。

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...

その出力を解析するために思いついたコードを次に示します。非常に厄介なコードであることはわかっていますが、前述したように、ここにある他のソリューションを Windows ストア アプリで機能させることはできませんでした。これはそうです。あなたの用途でそれをテストし、問題がある場合は言及してください。

***** ファイルまたはディレクトリを許可するように編集されました。*****

$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
于 2020-10-14T20:31:32.557 に答える