PowerShellでは、ジャンクション(シンボリックリンク)のターゲットパスを解決する必要があります。
たとえば、ターゲットがc:\someJunction
であるジャンクションc:\temp\target
があるとします。
$junc = Get-Item c:\someJunction
のバリエーションを試しましたが、c:\someJunction
しか取得できませんでした
特定のジャンクションのジャンクション(この例ではc:\temp\target
)のターゲットパスを見つけるにはどうすればよいですか?
次の手順でパスを取得できます。
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)
New-Item、Remove-Item、およびGet-ChildItemが拡張され、シンボリックリンクの作成と管理がサポートされるようになりました。 New-Itemの-ItemTypeパラメーターは、新しい値SymbolicLinkを受け入れます。 New-Itemコマンドレットを実行することにより、シンボリックリンクを1行で作成できるようになりました。
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:\
これは少ない作業でトリックを実行し、リモートサーバー上のジャンクションでも機能します。
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
}
この関数を使用することになります
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 ''
}
この質問に対するいくつかの本当に複雑な答えがあります!これは非常にシンプルで自明なものです:
(Get-Item C:\somejunction).Target
少なくとも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~
(他の回答にもこれが含まれていましたが、簡単には見えません/理解できません)