J. L. Doty
J. L. Doty

Reputation: 41

Using Powershell on Win 11, I need to get all the files in a root folder, excluding all files in a list of root-folder subfolders

Given a folder structure like this:

C:\rootFolder\D
C:\rootFolder\D\D
C:\rootFolder\D\E
C:\rootFolder\E
C:\rootFolder\E\D
C:\rootFolder\E\E
C:\rootFolder\F
C:\rootFolder\F\D
C:\rootFolder\F\E

And a List of subfolders to exclude in C:\rootFolder like this:

$excludeFolders = "C:\rootFolder\D", "C:\rootFolder\E"

I need to recursively get all the files in C:\rootFolder and its subfolders, excluding any files in $excludeFolders and their subfolders. I can't hard code $excludeFolders because they're being loaded from a text file. I've been able to do it with the following:

# 1. Get the files in the root Folder
Get-ChildItem -Path $rootFolder -File | Select-Object FullName
# 2. Get all the directories in the root folder
Get-ChildItem -Path $rootFolder -Directory | ForEach-Object {
    # Exclude those folders that are in the excludeFolders list
    if (-not ($excludeFolders -contains $_.FullName)) {
        #Process files in folders not in the excludeFolders list
        Get-ChildItem -Path $_.FullName -File -Recurse | Select-Object FullName
    }
}

But this 2-step approach is cumbersome and I'd prefer to do it in a single step. I've searched here and found something close using -Exclude, but that also eliminates the subfolders

C:\rootFolder\F\D
C:\rootFolder\F\E

Any help will be greatly appreciated.

Upvotes: 1

Views: 58

Answers (2)

Theo
Theo

Reputation: 61208

You can simplify your code and use just one Get-ChildItem call combined with a Where-Object clause to omit the files in the excluded folders like below:

$rootFolder = 'C:\rootFolder'

# read the folders to exclude from your text file, removing empty and whitespace only lines
# $excludeFolders = Get-Content -Path 'C:\SomeFolder\Excludes.txt' | Where-Object { $_ -match '\S' }

# for demo use:
$excludeFolders = 'C:\rootFolder\D', 'C:\rootFolder\E'

# next, create a regex of the folders to exclude
# each item will be Regex Escaped and joined together with the OR symbol '|'
$notThese = ($excludeFolders | ForEach-Object { [Regex]::Escape($_) }) -join '|'

# now just iterate the files and omit those in the folders to exclude
(Get-ChildItem -Path $rootFolder -File -Recurse |
 Where-Object { $_.DirectoryName -notmatch $notThese }).FullName

If the files or folder names inside the rootfolder may contain characters like [ or ], use -LiteralPath instead of -Path

Upvotes: 1

jdweng
jdweng

Reputation: 34421

You can recursively get files using code like below. The code below takes a file structure and creates an XML report of the files in the folder(s) enumerating one folder at a time. You can modify to exclude files/folders to meet your requirements.

using assembly System.Xml
using assembly System.Xml.Linq

Function WriteTree
{
    Param ([System.IO.DirectoryInfo]$info, [System.Xml.XmlWriter]$writer)
    begin{
        $size = [long]0;
        $writer.WriteStartElement("Folder");
    }
    process{
        try
        {

            $writer.WriteAttributeString('name', $info.Name);
            $writer.WriteAttributeString('numberSubFolders', $info.GetDirectories().Count.ToString());
            $writer.WriteAttributeString('numberFiles', $info.GetFiles().Count.ToString());
            $writer.WriteAttributeString('date', $info.LastWriteTime.ToString());
             
            foreach ($childInfo in $info.GetDirectories())
            {
                $size += WriteTree -info $childInfo -writer $writer;
            }
                
        }
        catch
        {
                $errorMsg = [string]::Format("Exception Folder : {0}, Error : {1}", $info.FullName, $_);
                Write-Host $errorMsg;
                $writer.WriteElementString('Error', $errorMsg);
        }

        $fileInfo = $null;
        try
        {
            $fileInfo = $info.GetFiles();
        }
        catch
        {
            $errorMsg = [string]::Format('Exception FileInfo : {0}, Error : {1}', $info.FullName, $_);
            Write-Host $errorMsg;
            $writer.WriteElementString('Error',$errorMsg);
        }

        if($fileInfo -ne $null)
        {
            foreach ($finfo in $fileInfo)
            {
                try
                {
                    $writer.WriteStartElement('File');
                    $writer.WriteAttributeString('name', $finfo.Name);
                    $writer.WriteAttributeString('size', $finfo.Length.ToString());
                    $writer.WriteAttributeString('date', $info.LastWriteTime.ToString());
                    $writer.WriteEndElement();
                    $size += $finfo.Length;
                }
                catch
                {
                    $errorMsg = [string]::Format('Exception File : {0}, Error : {1}', $finfo.FullName, $_);
                    Write-Host $errorMsg;
                    $writer.WriteElementString('Error', $errorMsg);
                }
            }
        }

        $writer.WriteElementString('size', $size.ToString());
        $writer.WriteEndElement();
        return $size;
    }
    end{}
}

$filename = 'c:\temp\test.xml'
$folder = 'c:\temp'
$writer = $null

$settings = [System.Xml.XmlWriterSettings]::new();
$settings.Indent = $true;

$writer = [System.Xml.XmlWriter]::Create($filename, $settings);
$writer.WriteStartDocument($true);

$info = [System.IO.DirectoryInfo]::new($folder);

WriteTree -info $info -writer $writer;

$writer.WriteEndDocument();
$writer.Flush();
$writer.Close();
Read-Host 'Enter Return'

Upvotes: -2

Related Questions