When 'Remove-Item' is used to remove a symbolic link in Windows, only the link itself is removed. The '-Force' switch is no longer required. If the directory pointed to by the link has child items, the cmdlet no longer prompts the user to remove the child items---those child items are not removed. The '-Recurse' switch, if given, is ignored. This brings 'Remove-Item' more in line with the behavior of the 'rm' command on Unix.
This commit is contained in:
parent
a2268ab3ec
commit
f1769fe7a8
@ -3256,7 +3256,7 @@ namespace Microsoft.PowerShell.Commands
|
||||
try
|
||||
{
|
||||
System.IO.DirectoryInfo di = new System.IO.DirectoryInfo(providerPath);
|
||||
if (!Platform.IsWindows && di != null && (di.Attributes & System.IO.FileAttributes.ReparsePoint) != 0)
|
||||
if (di != null && (di.Attributes & System.IO.FileAttributes.ReparsePoint) != 0)
|
||||
{
|
||||
shouldRecurse = false;
|
||||
treatAsFile = true;
|
||||
|
@ -2863,15 +2863,6 @@ namespace Microsoft.PowerShell.Commands
|
||||
continueRemoval = ShouldProcess(directory.FullName, action);
|
||||
}
|
||||
|
||||
//if this is a reparse point and force is not specified then warn user but dont remove the directory.
|
||||
if (Platform.IsWindows && ((directory.Attributes & FileAttributes.ReparsePoint) != 0) && !Force)
|
||||
{
|
||||
String error = StringUtil.Format(FileSystemProviderStrings.DirectoryReparsePoint, directory.FullName);
|
||||
Exception e = new IOException(error);
|
||||
WriteError(new ErrorRecord(e, "DirectoryNotEmpty", ErrorCategory.WriteError, directory));
|
||||
return;
|
||||
}
|
||||
|
||||
if ((directory.Attributes & FileAttributes.ReparsePoint) != 0)
|
||||
{
|
||||
bool success = InternalSymbolicLinkLinkCodeMethods.DeleteJunction(directory.FullName);
|
||||
@ -3327,13 +3318,14 @@ namespace Microsoft.PowerShell.Commands
|
||||
path = NormalizePath(path);
|
||||
|
||||
// First check to see if it is a directory
|
||||
|
||||
try
|
||||
{
|
||||
DirectoryInfo directory = new DirectoryInfo(path);
|
||||
|
||||
// If the above didn't throw an exception, check to
|
||||
// see if it contains any directories
|
||||
// see if we should proceed and if it contains any children
|
||||
if ((directory.Attributes & FileAttributes.Directory) != FileAttributes.Directory)
|
||||
return false;
|
||||
|
||||
result = DirectoryInfoHasChildItems(directory);
|
||||
}
|
||||
|
@ -1,17 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
@ -26,36 +26,36 @@
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
@ -216,9 +216,6 @@
|
||||
<data name="DirectoryExist" xml:space="preserve">
|
||||
<value>An item with the specified name {0} already exists.</value>
|
||||
</data>
|
||||
<data name="DirectoryReparsePoint" xml:space="preserve">
|
||||
<value>{0} is an NTFS junction point. Use the Force parameter to delete or modify this object.</value>
|
||||
</data>
|
||||
<data name="BasePathLengthError" xml:space="preserve">
|
||||
<value>The path length is too short. The character length of a path cannot be less than the character length of the basePath.</value>
|
||||
</data>
|
||||
|
@ -246,6 +246,186 @@ Describe "Basic FileSystem Provider Tests" -Tags "CI" {
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Hard link and symbolic link tests" -Tags "CI", "RequireAdminOnWindows" {
|
||||
BeforeAll {
|
||||
# on macOS, the /tmp directory is a symlink, so we'll resolve it here
|
||||
$TestPath = $TestDrive
|
||||
if ($IsOSX)
|
||||
{
|
||||
$item = Get-Item $TestPath
|
||||
$dirName = $item.BaseName
|
||||
$item = Get-Item $item.PSParentPath
|
||||
if ($item.LinkType -eq "SymbolicLink")
|
||||
{
|
||||
$TestPath = Join-Path $item.Target $dirName
|
||||
}
|
||||
}
|
||||
|
||||
$realFile = Join-Path $TestPath "file.txt"
|
||||
$nonFile = Join-Path $TestPath "not-a-file"
|
||||
$fileContent = "some text"
|
||||
$realDir = Join-Path $TestPath "subdir"
|
||||
$nonDir = Join-Path $TestPath "not-a-dir"
|
||||
$hardLinkToFile = Join-Path $TestPath "hard-to-file.txt"
|
||||
$symLinkToFile = Join-Path $TestPath "sym-link-to-file.txt"
|
||||
$symLinkToDir = Join-Path $TestPath "sym-link-to-dir"
|
||||
$symLinkToNothing = Join-Path $TestPath "sym-link-to-nowhere"
|
||||
$dirSymLinkToDir = Join-Path $TestPath "symd-link-to-dir"
|
||||
$junctionToDir = Join-Path $TestPath "junction-to-dir"
|
||||
|
||||
New-Item -ItemType File -Path $realFile -Value $fileContent >$null
|
||||
New-Item -ItemType Directory -Path $realDir >$null
|
||||
}
|
||||
|
||||
Context "New-Item and hard/symbolic links" {
|
||||
It "New-Item can create a hard link to a file" {
|
||||
New-Item -ItemType HardLink -Path $hardLinkToFile -Value $realFile
|
||||
Test-Path $hardLinkToFile | Should Be $true
|
||||
$link = Get-Item -Path $hardLinkToFile
|
||||
$link.LinkType | Should BeExactly "HardLink"
|
||||
Get-Content -Path $hardLinkToFile | Should be $fileContent
|
||||
}
|
||||
It "New-Item can create symbolic link to file" {
|
||||
New-Item -ItemType SymbolicLink -Path $symLinkToFile -Value $realFile
|
||||
Test-Path $symLinkToFile | Should Be $true
|
||||
$real = Get-Item -Path $realFile
|
||||
$link = Get-Item -Path $symLinkToFile
|
||||
$link.LinkType | Should BeExactly "SymbolicLink"
|
||||
$link.Target | Should Be $real.FullName
|
||||
Get-Content -Path $symLinkToFile | Should be $fileContent
|
||||
}
|
||||
It "New-Item can create a symbolic link to nothing" {
|
||||
New-Item -ItemType SymbolicLink -Path $symLinkToNothing -Value $nonFile
|
||||
Test-Path $symLinkToNothing | Should Be $true
|
||||
$link = Get-Item -Path $symLinkToNothing
|
||||
$link.LinkType | Should BeExactly "SymbolicLink"
|
||||
$link.Target | Should Be $nonFile
|
||||
}
|
||||
It "New-Item can create a symbolic link to a directory" -Skip:($IsWindows) {
|
||||
New-Item -ItemType SymbolicLink -Path $symLinkToDir -Value $realDir
|
||||
Test-Path $symLinkToDir | Should Be $true
|
||||
$real = Get-Item -Path $realDir
|
||||
$link = Get-Item -Path $symLinkToDir
|
||||
$link.LinkType | Should BeExactly "SymbolicLink"
|
||||
$link.Target | Should Be $real.FullName
|
||||
}
|
||||
It "New-Item can create a directory symbolic link to a directory" -Skip:(-Not $IsWindows) {
|
||||
New-Item -ItemType SymbolicLink -Path $symLinkToDir -Value $realDir
|
||||
Test-Path $symLinkToDir | Should Be $true
|
||||
$real = Get-Item -Path $realDir
|
||||
$link = Get-Item -Path $symLinkToDir
|
||||
$link | Should BeOfType System.IO.DirectoryInfo
|
||||
$link.LinkType | Should BeExactly "SymbolicLink"
|
||||
$link.Target | Should Be $real.FullName
|
||||
}
|
||||
It "New-Item can create a directory junction to a directory" -Skip:(-Not $IsWindows) {
|
||||
New-Item -ItemType Junction -Path $junctionToDir -Value $realDir
|
||||
Test-Path $junctionToDir | Should Be $true
|
||||
}
|
||||
}
|
||||
|
||||
Context "Remove-Item and hard/symbolic links" {
|
||||
BeforeAll {
|
||||
$testCases = @(
|
||||
@{
|
||||
Name = "Remove-Item can remove a hard link to a file"
|
||||
Link = $hardLinkToFile
|
||||
Target = $realFile
|
||||
}
|
||||
@{
|
||||
Name = "Remove-Item can remove a symbolic link to a file"
|
||||
Link = $symLinkToFile
|
||||
Target = $realFile
|
||||
}
|
||||
)
|
||||
|
||||
# New-Item on Windows will not create a "plain" symlink to a directory
|
||||
$unixTestCases = @(
|
||||
@{
|
||||
Name = "Remove-Item can remove a symbolic link to a directory on Unix"
|
||||
Link = $symLinkToDir
|
||||
Target = $realDir
|
||||
}
|
||||
)
|
||||
|
||||
# Junctions and directory symbolic links are Windows and NTFS only
|
||||
$windowsTestCases = @(
|
||||
@{
|
||||
Name = "Remove-Item can remove a symbolic link to a directory on Windows"
|
||||
Link = $symLinkToDir
|
||||
Target = $realDir
|
||||
}
|
||||
@{
|
||||
Name = "Remove-Item can remove a directory symbolic link to a directory on Windows"
|
||||
Link = $dirSymLinkToDir
|
||||
Target = $realDir
|
||||
}
|
||||
@{
|
||||
Name = "Remove-Item can remove a junction to a directory"
|
||||
Link = $junctionToDir
|
||||
Target = $realDir
|
||||
}
|
||||
)
|
||||
|
||||
function TestRemoveItem
|
||||
{
|
||||
Param (
|
||||
[string]$Link,
|
||||
[string]$Target
|
||||
)
|
||||
|
||||
Remove-Item -Path $Link -ErrorAction SilentlyContinue >$null
|
||||
Test-Path -Path $Link | Should Be $false
|
||||
Test-Path -Path $Target | Should Be $true
|
||||
}
|
||||
}
|
||||
|
||||
It "<Name>" -TestCases $testCases {
|
||||
Param (
|
||||
[string]$Name,
|
||||
[string]$Link,
|
||||
[string]$Target
|
||||
)
|
||||
|
||||
TestRemoveItem $Link $Target
|
||||
}
|
||||
|
||||
It "<Name>" -TestCases $unixTestCases -Skip:($IsWindows) {
|
||||
Param (
|
||||
[string]$Name,
|
||||
[string]$Link,
|
||||
[string]$Target
|
||||
)
|
||||
|
||||
TestRemoveItem $Link $Target
|
||||
}
|
||||
|
||||
It "<Name>" -TestCases $windowsTestCases -Skip:(-not $IsWindows) {
|
||||
Param (
|
||||
[string]$Name,
|
||||
[string]$Link,
|
||||
[string]$Target
|
||||
)
|
||||
|
||||
TestRemoveItem $Link $Target
|
||||
}
|
||||
|
||||
It "Remove-Item ignores -Recurse switch when deleting symlink to directory" {
|
||||
$folder = Join-Path $TestDrive "folder"
|
||||
$file = Join-Path $TestDrive "folder" "file"
|
||||
$link = Join-Path $TestDrive "sym-to-folder"
|
||||
New-Item -ItemType Directory -Path $folder >$null
|
||||
New-Item -ItemType File -Path $file -Value "some content" >$null
|
||||
New-Item -ItemType SymbolicLink -Path $link -value $folder >$null
|
||||
$childA = Get-Childitem $folder
|
||||
Remove-Item -Path $link -Recurse
|
||||
$childB = Get-ChildItem $folder
|
||||
$childB.Count | Should Be 1
|
||||
$childB.Count | Should BeExactly $childA.Count
|
||||
$childB.Name | Should BeExactly $childA.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Copy-Item can avoid copying an item onto itself" -Tags "CI", "RequireAdminOnWindows" {
|
||||
BeforeAll {
|
||||
|
Loading…
Reference in New Issue
Block a user