diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/ResolvePathCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/ResolvePathCommand.cs index 905a82c8e86..06e8b3f7c69 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/ResolvePathCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/ResolvePathCommand.cs @@ -22,6 +22,8 @@ public class ResolvePathCommand : CoreCommandWithCredentialsBase /// [Parameter(Position = 0, ParameterSetName = "Path", Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] + [Parameter(Position = 0, ParameterSetName = "PathWithRelativeBase", + Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public string[] Path { get @@ -40,6 +42,8 @@ public string[] Path /// [Parameter(ParameterSetName = "LiteralPath", Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] + [Parameter(ParameterSetName = "LiteralPathWithRelativeBase", + Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] [Alias("PSPath", "LP")] public string[] LiteralPath { @@ -59,7 +63,8 @@ public string[] LiteralPath /// Gets or sets the value that determines if the resolved path should /// be resolved to its relative version. /// - [Parameter()] + [Parameter(ParameterSetName = "Path")] + [Parameter(ParameterSetName = "LiteralPath")] public SwitchParameter Relative { get @@ -75,6 +80,25 @@ public SwitchParameter Relative private SwitchParameter _relative; + /// + /// Gets or sets the path the resolved relative path should be based off. + /// + [Parameter(Mandatory = true, ParameterSetName = "PathWithRelativeBase")] + [Parameter(Mandatory = true, ParameterSetName = "LiteralPathWithRelativeBase")] + public string RelativeBasePath + { + get + { + return _relativeBasePath; + } + + set + { + _relative = true; + _relativeBasePath = value; + } + } + #endregion Parameters #region parameter data @@ -84,10 +108,68 @@ public SwitchParameter Relative /// private string[] _paths; + private PSDriveInfo _relativeDrive; + private string _relativeBasePath; + #endregion parameter data #region Command code + /// + /// Finds the path and drive that should be used for relative path resolution + /// represents. + /// + protected override void BeginProcessing() + { + if (_relative) + { + if (!string.IsNullOrEmpty(RelativeBasePath)) + { + try + { + _relativeBasePath = SessionState.Internal.Globber.GetProviderPath(RelativeBasePath, CmdletProviderContext, out _, out _relativeDrive); + } + catch (ProviderNotFoundException providerNotFound) + { + ThrowTerminatingError( + new ErrorRecord( + providerNotFound.ErrorRecord, + providerNotFound)); + } + catch (DriveNotFoundException driveNotFound) + { + ThrowTerminatingError( + new ErrorRecord( + driveNotFound.ErrorRecord, + driveNotFound)); + } + catch (ProviderInvocationException providerInvocation) + { + ThrowTerminatingError( + new ErrorRecord( + providerInvocation.ErrorRecord, + providerInvocation)); + } + catch (NotSupportedException notSupported) + { + ThrowTerminatingError( + new ErrorRecord(notSupported, "ProviderIsNotNavigationCmdletProvider", ErrorCategory.InvalidArgument, RelativeBasePath)); + } + catch (InvalidOperationException invalidOperation) + { + ThrowTerminatingError( + new ErrorRecord(invalidOperation, "InvalidHomeLocation", ErrorCategory.InvalidOperation, RelativeBasePath)); + } + + return; + } + + _relativeDrive = SessionState.Path.CurrentLocation.Drive; + _relativeBasePath = SessionState.Path.CurrentLocation.ProviderPath; + return; + } + } + /// /// Resolves the path containing glob characters to the PowerShell paths that it /// represents. @@ -109,10 +191,9 @@ protected override void ProcessRecord() { // When result path and base path is on different PSDrive // (../)*path should not go beyond the root of base path - if (currentPath.Drive != SessionState.Path.CurrentLocation.Drive && - SessionState.Path.CurrentLocation.Drive != null && - !currentPath.ProviderPath.StartsWith( - SessionState.Path.CurrentLocation.Drive.Root, StringComparison.OrdinalIgnoreCase)) + if (currentPath.Drive != _relativeDrive && + _relativeDrive != null && + !currentPath.ProviderPath.StartsWith(_relativeDrive.Root, StringComparison.OrdinalIgnoreCase)) { WriteObject(currentPath.Path, enumerateCollection: false); continue; @@ -127,8 +208,7 @@ protected override void ProcessRecord() } baseCache = basePath; - string adjustedPath = SessionState.Path.NormalizeRelativePath(currentPath.Path, - SessionState.Path.CurrentLocation.ProviderPath); + string adjustedPath = SessionState.Path.NormalizeRelativePath(currentPath.Path, _relativeBasePath); // Do not insert './' if result path is not relative if (!adjustedPath.StartsWith( diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Resolve-Path.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Resolve-Path.Tests.ps1 index 87195c26352..91a16eb507b 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Resolve-Path.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Resolve-Path.Tests.ps1 @@ -48,4 +48,8 @@ Describe "Resolve-Path returns proper path" -Tag "CI" { Pop-Location } } + It 'Resolve-Path should support user specified base paths' { + $Expected = Join-Path -Path .\ -ChildPath fakeroot + Resolve-Path -Path $fakeRoot -RelativeBasePath $testRoot | Should -BeExactly $Expected + } }