web-dev-qa-db-ja.com

ジャンクションターゲットパスを解決するためのPowerShell

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

たとえば、ターゲットがc:\someJunctionであるジャンクションc:\temp\targetがあるとします。

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

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

25
ash

次の手順でパスを取得できます。

Get-ChildItem -Path C:\someJunction

フォルダの内容ではなくパスを見つけるために編集

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)
5
Josh

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:\
37

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

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

この関数を使用することになります

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 '' 
}
3

この質問に対するいくつかの本当に複雑な答えがあります!これは非常にシンプルで自明なものです:

(Get-Item C:\somejunction).Target
1
Jordan Shurmer

少なくともPSv5では、これと同じくらい簡単に、いくつかのdirsリンクのすべてのターゲットをリストし(またはさらに1つ下に)、オブジェクトとして取得し、適切にフォーマットします(たとえば、すべての*~ dirsは実際にはジャンクションです):

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

1つのリンクの場合:

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

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

または(ターゲットをC:\Jaspersoft\Apache-websrv~ジャンクションの文字列値として取得するため):

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

例では、標準のlsは次のようになります。

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~

(他の回答にもこれが含まれていましたが、簡単には見えません/理解できません)

0