From caad7ed471d46629f155a2c7ccbbafa4ffc29355 Mon Sep 17 00:00:00 2001 From: kwkam Date: Sat, 10 Nov 2018 03:08:26 +0800 Subject: [PATCH] Fix Rename-Item -Path with wildcard char (#7398) --- .../commands/management/Navigation.cs | 79 +++++++++++++++++-- .../namespaces/LocationGlobber.cs | 8 +- .../FunctionProvider.Tests.ps1 | 6 +- .../Rename-Item.Tests.ps1 | 43 +++++++++- 4 files changed, 124 insertions(+), 12 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Navigation.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Navigation.cs index 0df846088..eedbeb485 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Navigation.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Navigation.cs @@ -3519,22 +3519,87 @@ namespace Microsoft.PowerShell.Commands #region Command code + private Collection GetResolvedPaths(string path) + { + Collection results = null; + try + { + results = SessionState.Path.GetResolvedPSPathFromPSPath(path, CmdletProviderContext); + } + catch (PSNotSupportedException notSupported) + { + WriteError( + new ErrorRecord( + notSupported.ErrorRecord, + notSupported)); + } + catch (DriveNotFoundException driveNotFound) + { + WriteError( + new ErrorRecord( + driveNotFound.ErrorRecord, + driveNotFound)); + } + catch (ProviderNotFoundException providerNotFound) + { + WriteError( + new ErrorRecord( + providerNotFound.ErrorRecord, + providerNotFound)); + } + catch (ItemNotFoundException pathNotFound) + { + WriteError( + new ErrorRecord( + pathNotFound.ErrorRecord, + pathNotFound)); + } + + return results; + } + /// /// Moves the specified item to the specified destination /// protected override void ProcessRecord() + { + if (SuppressWildcardExpansion) + { + RenameItem(Path, literalPath: true); + return; + } + + Collection resolvedPaths = GetResolvedPaths(Path); + + if (resolvedPaths == null) + { + return; + } + + if (resolvedPaths.Count == 1) + { + RenameItem(resolvedPaths[0].Path, literalPath: true); + } + else + { + RenameItem(WildcardPattern.Unescape(Path), literalPath: true); + } + } + + private void RenameItem(string path, bool literalPath = false) { CmdletProviderContext currentContext = CmdletProviderContext; + currentContext.SuppressWildcardExpansion = literalPath; try { - if (!InvokeProvider.Item.Exists(Path, currentContext)) + if (!InvokeProvider.Item.Exists(path, currentContext)) { PSInvalidOperationException invalidOperation = (PSInvalidOperationException) PSTraceSource.NewInvalidOperationException( NavigationResources.RenameItemDoesntExist, - Path); + path); WriteError( new ErrorRecord( @@ -3580,7 +3645,7 @@ namespace Microsoft.PowerShell.Commands bool isCurrentLocationOrAncestor = false; try { - isCurrentLocationOrAncestor = SessionState.Path.IsCurrentLocationOrAncestor(_path, currentContext); + isCurrentLocationOrAncestor = SessionState.Path.IsCurrentLocationOrAncestor(path, currentContext); } catch (PSNotSupportedException notSupported) { @@ -3621,7 +3686,7 @@ namespace Microsoft.PowerShell.Commands (PSInvalidOperationException) PSTraceSource.NewInvalidOperationException( NavigationResources.RenamedItemInUse, - Path); + path); WriteError( new ErrorRecord( @@ -3635,12 +3700,12 @@ namespace Microsoft.PowerShell.Commands currentContext.PassThru = PassThru; - tracer.WriteLine("Rename {0} to {1}", Path, NewName); + tracer.WriteLine("Rename {0} to {1}", path, NewName); try { // Now do the rename - InvokeProvider.Item.Rename(Path, NewName, currentContext); + InvokeProvider.Item.Rename(path, NewName, currentContext); } catch (PSNotSupportedException notSupported) { @@ -3674,7 +3739,7 @@ namespace Microsoft.PowerShell.Commands pathNotFound)); return; } - } // ProcessRecord + } #endregion Command code } // RenameItemCommand diff --git a/src/System.Management.Automation/namespaces/LocationGlobber.cs b/src/System.Management.Automation/namespaces/LocationGlobber.cs index 22ae25e8b..ba4b05d42 100644 --- a/src/System.Management.Automation/namespaces/LocationGlobber.cs +++ b/src/System.Management.Automation/namespaces/LocationGlobber.cs @@ -433,7 +433,13 @@ namespace System.Management.Automation s_pathResolutionTracer.WriteLine("Path is DRIVE-QUALIFIED"); - string relativePath = GetDriveRootRelativePathFromPSPath(path, context, true, out drive, out providerInstance); + string relativePath = + GetDriveRootRelativePathFromPSPath( + path, + context, + !context.SuppressWildcardExpansion, + out drive, + out providerInstance); Dbg.Diagnostics.Assert( drive != null, diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/FunctionProvider.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/FunctionProvider.Tests.ps1 index b21a8977b..f8796f330 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/FunctionProvider.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/FunctionProvider.Tests.ps1 @@ -104,7 +104,7 @@ Describe "Basic Function Provider Tests" -Tags "CI" { } It "Fails to rename not existing function" { - { Rename-Item $nonExistingFunction -NewName $newName -ErrorAction Stop } | Should -Throw -ErrorId "InvalidOperation,Microsoft.PowerShell.Commands.RenameItemCommand" + { Rename-Item $nonExistingFunction -NewName $newName -ErrorAction Stop } | Should -Throw -ErrorId "PathNotFound,Microsoft.PowerShell.Commands.RenameItemCommand" } It "Fails to rename function which is Constant" { @@ -113,8 +113,8 @@ Describe "Basic Function Provider Tests" -Tags "CI" { } It "Fails to rename function which is ReadOnly" { - Set-Item $nonExistingFunction -Options "ReadOnly" - { Rename-Item $nonExistingFunction -NewName $newName -ErrorAction Stop } | Should -Throw -ErrorId "InvalidOperation,Microsoft.PowerShell.Commands.RenameItemCommand" + Set-Item $nonExistingFunction -Options "ReadOnly" -Value $functionValue + { Rename-Item $nonExistingFunction -NewName $newName -ErrorAction Stop } | Should -Throw -ErrorId "CannotRenameFunction,Microsoft.PowerShell.Commands.RenameItemCommand" } It "Renames ReadOnly function when -Force parameter is on" { diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Rename-Item.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Rename-Item.Tests.ps1 index b73461caf..7abedaa31 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Rename-Item.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Rename-Item.Tests.ps1 @@ -2,10 +2,15 @@ # Licensed under the MIT License. Describe "Rename-Item tests" -Tag "CI" { BeforeAll { - $content = "This is content" Setup -f originalFile.txt -content "This is content" $source = "$TESTDRIVE/originalFile.txt" $target = "$TESTDRIVE/ItemWhichHasBeenRenamed.txt" + Setup -f [orig-file].txt -content "This is not content" + $sourceSp = "$TestDrive/``[orig-file``].txt" + $targetSpName = "ItemWhichHasBeen[Renamed].txt" + $targetSp = "$TestDrive/ItemWhichHasBeen``[Renamed``].txt" + Setup -Dir [test-dir] + $wdSp = "$TestDrive/``[test-dir``]" } It "Rename-Item will rename a file" { Rename-Item $source $target @@ -13,4 +18,40 @@ Describe "Rename-Item tests" -Tag "CI" { test-path $target | Should -BeTrue "$target" | Should -FileContentMatchExactly "This is content" } + It "Rename-Item will rename a file when path contains special char" { + Rename-Item $sourceSp $targetSpName + $sourceSp | Should -Not -Exist + $targetSp | Should -Exist + $targetSp | Should -FileContentMatchExactly "This is not content" + } + It "Rename-Item will rename a file when -Path and CWD contains special char" { + $content = "This is content" + $oldSpName = "[orig]file.txt" + $oldSpBName = "``[orig``]file.txt" + $oldSp = "$wdSp/$oldSpBName" + $newSpName = "[renamed]file.txt" + $newSp = "$wdSp/``[renamed``]file.txt" + In $wdSp -Execute { + $null = New-Item -Name $oldSpName -ItemType File -Value $content -Force + Rename-Item -Path $oldSpBName $newSpName + } + $oldSp | Should -Not -Exist + $newSp | Should -Exist + $newSp | Should -FileContentMatchExactly $content + } + It "Rename-Item will rename a file when -LiteralPath and CWD contains special char" { + $content = "This is not content" + $oldSpName = "[orig]file2.txt" + $oldSpBName = "``[orig``]file2.txt" + $oldSp = "$wdSp/$oldSpBName" + $newSpName = "[renamed]file2.txt" + $newSp = "$wdSp/``[renamed``]file2.txt" + In $wdSp -Execute { + $null = New-Item -Name $oldSpName -ItemType File -Value $content -Force + Rename-Item -LiteralPath $oldSpName $newSpName + } + $oldSp | Should -Not -Exist + $newSp | Should -Exist + $newSp | Should -FileContentMatchExactly $content + } }