Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 89 additions & 46 deletions src/System.Management.Automation/engine/SessionStateContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1323,52 +1323,7 @@ internal void GetChildItems(

try
{
// If we're recursing, do some path fixups to match user
// expectations, but only if the last part is a file and not a directory:
if (recurse && !path.EndsWith(Path.DirectorySeparatorChar) && !path.EndsWith(Path.AltDirectorySeparatorChar))

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The removed code here was actually moved to the NormalizePathForChildItemRecursion method below, with no change at all.

{
string childName = GetChildName(path, context);

// If -File or -Directory is specified and path is ended with '*', we should include the parent path as search path

bool isFileOrDirectoryPresent = false;

if (context.DynamicParameters is Microsoft.PowerShell.Commands.GetChildDynamicParameters dynParam)
{
isFileOrDirectoryPresent = dynParam.File.IsPresent || dynParam.Directory.IsPresent;
}

if (string.Equals(childName, "*", StringComparison.OrdinalIgnoreCase) && isFileOrDirectoryPresent)
{
string parentName = path.Substring(0, path.Length - childName.Length);
path = parentName;
}
// dir c:\tem* -include *.ps1 -rec => No change
if ((context.Include == null) || (context.Include.Count == 0))
{
// dir c:\tem* -rec => dir c:\ -include tem* -rec
// dir tem* -rec => dir -include tem* -rec
// dir temp -rec

// Should glob paths and files that match tem*, but then
// recurse into all subdirectories and do the same for
// those directories.
if (!string.IsNullOrEmpty(path) && !IsItemContainer(path))
{
if (!string.Equals(childName, "*", StringComparison.OrdinalIgnoreCase))
{
if (context.Include != null)
{
context.Include.Add(childName);
modifiedInclude = true;
}
}

string parentName = path.Substring(0, path.Length - childName.Length);
path = parentName;
}
}
}
modifiedInclude = NormalizePathForChildItemRecursion(ref path, recurse, context);

// Save the include and exclude filters so that we can ignore
// them when doing recursion
Expand Down Expand Up @@ -1529,6 +1484,63 @@ internal void GetChildItems(
}
}

private bool NormalizePathForChildItemRecursion(
ref string path,
bool recurse,
CmdletProviderContext context)
{
bool modifiedInclude = false;

// If we're recursing, do some path fixups to match user
// expectations, but only if the last part is a file and not a directory:
if (recurse && !path.EndsWith(Path.DirectorySeparatorChar) && !path.EndsWith(Path.AltDirectorySeparatorChar))
{
string childName = GetChildName(path, context);

// If -File or -Directory is specified and path is ended with '*', we should include the parent path as search path

bool isFileOrDirectoryPresent = false;

if (context.DynamicParameters is Microsoft.PowerShell.Commands.GetChildDynamicParameters dynParam)
{
isFileOrDirectoryPresent = dynParam.File.IsPresent || dynParam.Directory.IsPresent;
}

if (string.Equals(childName, "*", StringComparison.OrdinalIgnoreCase) && isFileOrDirectoryPresent)
{
string parentName = path.Substring(0, path.Length - childName.Length);
path = parentName;
}
// dir c:\tem* -include *.ps1 -rec => No change
if ((context.Include == null) || (context.Include.Count == 0))
{
// dir c:\tem* -rec => dir c:\ -include tem* -rec
// dir tem* -rec => dir -include tem* -rec
// dir temp -rec

// Should glob paths and files that match tem*, but then
// recurse into all subdirectories and do the same for
// those directories.
if (!string.IsNullOrEmpty(path) && !IsItemContainer(path))
{
if (!string.Equals(childName, "*", StringComparison.OrdinalIgnoreCase))
{
if (context.Include != null)
{
context.Include.Add(childName);
modifiedInclude = true;
}
}

string parentName = path.Substring(0, path.Length - childName.Length);
path = parentName;
}
}
}

return modifiedInclude;
}

/// <summary>
/// Gets the child items of the item at the specified path.
/// </summary>
Expand Down Expand Up @@ -2229,6 +2241,10 @@ internal Collection<string> GetChildNames(
}

CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext);
context.SetFilters(
include: new Collection<string>(),
exclude: new Collection<string>(),
filter: null);
context.Force = force;
context.SuppressWildcardExpansion = literalPath;

Expand Down Expand Up @@ -2319,6 +2335,33 @@ internal void GetChildNames(
throw PSTraceSource.NewArgumentNullException(nameof(path));
}

bool modifiedInclude = false;

try
{
if ((recurse && !context.SuppressWildcardExpansion) || LocationGlobber.ShouldPerformGlobbing(path, context))
{
modifiedInclude = NormalizePathForChildItemRecursion(ref path, recurse, context);
}

GetChildNamesCore(path, returnContainers, recurse, depth, context);
}
finally
{
if (modifiedInclude)
{
context.Include.Clear();
}
}
}

private void GetChildNamesCore(
string path,
ReturnContainers returnContainers,
bool recurse,
uint depth,
CmdletProviderContext context)
{
// Construct the include filter

Collection<WildcardPattern> includeMatcher =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,77 @@ Describe "Get-ChildItem" -Tags "CI" {
(Get-ChildItem -Path $TestDrive -Depth 1 -Exclude $item_a).Count | Should -Be 7
}

It "Should return names recursively when the path has a wildcard leaf" {
$root = New-Item -Path $TestDrive -Name "NameRecurse" -ItemType Directory
try {
$child = New-Item -Path $root -Name "Child" -ItemType Directory
$null = New-Item -Path $root -Name "root.md" -ItemType File
$null = New-Item -Path $child -Name "child.md" -ItemType File
$null = New-Item -Path $child -Name "ignored.txt" -ItemType File
$path = Join-Path $root "*.md"

$result = Get-ChildItem -Path $path -Recurse -File -Name
$result | Should -HaveCount 2
$result | Should -Contain "root.md"
$result | Should -Contain "Child$([IO.Path]::DirectorySeparatorChar)child.md"

Get-ChildItem -Path $path -Depth 0 -File -Name | Should -BeExactly "root.md"
}
finally {
Remove-Item -LiteralPath $root -Recurse -Force
}
}

It "Should preserve recursive wildcard filters through the public GetNames API" {
$root = New-Item -Path $TestDrive -Name "PublicGetNamesRecurse" -ItemType Directory
try {
$child = New-Item -Path $root -Name "Child" -ItemType Directory
$null = New-Item -Path $root -Name "root.md" -ItemType File
$null = New-Item -Path $child -Name "child.md" -ItemType File
$null = New-Item -Path $child -Name "ignored.txt" -ItemType File
$path = Join-Path $root "*.md"

$result = $ExecutionContext.SessionState.InvokeProvider.ChildItem.GetNames(
$path,
[System.Management.Automation.ReturnContainers]::ReturnMatchingContainers,
$true)

$result | Should -HaveCount 2
$result | Should -Contain "root.md"
$result | Should -Contain "Child$([IO.Path]::DirectorySeparatorChar)child.md"
}
finally {
Remove-Item -LiteralPath $root -Recurse -Force
}
}

It "Should resolve recursive leaf paths from the current directory" {
$root = New-Item -Path $TestDrive -Name "CurrentDirectoryRecurse" -ItemType Directory
try {
$child = New-Item -Path $root -Name "Child" -ItemType Directory
$rootFile = New-Item -Path $root -Name "root.md" -ItemType File
$childFile = New-Item -Path $child -Name "child.md" -ItemType File

Push-Location $root
try {
$result = Get-ChildItem *.md -Recurse -File -Name
$result | Should -HaveCount 2
$result | Should -Contain $rootFile.Name
$result | Should -Contain "Child$([IO.Path]::DirectorySeparatorChar)$($childFile.Name)"

$result = Get-ChildItem child.md -Recurse -File -Name
$result | Should -HaveCount 1
$result | Should -BeExactly "Child$([IO.Path]::DirectorySeparatorChar)$($childFile.Name)"
}
finally {
Pop-Location
}
}
finally {
Remove-Item -LiteralPath $root -Recurse -Force
}
}

It "Should return items recursively when using 'Include' or 'Exclude' parameters with -LiteralPath" {
(Get-ChildItem -LiteralPath $TestDrive -Recurse -Exclude *).Count | Should -Be 0
(Get-ChildItem -LiteralPath $TestDrive -Recurse -Include *.dll).Count | Should -Be (Get-ChildItem $TestDrive -Recurse -Include *.dll).Count
Expand Down
Loading