Functions/GenXdev.FileSystem/Find-Item.Cmdlet.cs
// ################################################################################
// Part of PowerShell module : GenXdev.FileSystem // Original cmdlet filename : Find-Item.Cmdlet.cs // Original author : René Vaessen / GenXdev // Version : 1.276.2025 // ################################################################################ // MIT License // // Copyright 2021-2025 GenXdev // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // ################################################################################ using StreamRegex.Extensions.Core; using StreamRegex.Extensions.RegexExtensions; using System.Collections.Concurrent; using System.Collections.ObjectModel; using System.Data.Common; using System.Diagnostics; using System.Management; using System.Management.Automation; using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Xml.Linq; using Windows.ApplicationModel.Calls; /// <summary> /// <para type="synopsis"> /// Fast multi-threaded file and directory search with optional textcontent pattern matching /// capabilities. /// </para> /// /// <para type="description"> /// PARAMETERS /// </para> /// /// <para type="description"> /// -Name <String[]><br/> /// File name or pattern to search for. Default is '*'.<br/> /// - <b>Aliases</b>: like, l, Path, Query, SearchMask<br/> /// - <b>Position</b>: 0<br/> /// - <b>Default</b>: "*"<br/> /// - <b>Features</b>:<br/> /// - Supports wildcards: <c>*</c> and <c>?</c> for pattern matching<br/> /// - Directory Navigation: <c>\</c> and <c>/</c> for path separators<br/> /// - Recursive pattern: <c>\**\</c> for recursive directory matching<br/> /// - Supports file names without paths, relative paths, absolute paths, UNC paths, rooted paths with drive letters, and base directory patterns<br/> /// - Alternate Data Streams: <c>file.txt:stream</c><br/> /// - <b>Examples</b>:<br/> /// - Simple wildcard: <c>*.txt</c> - All txt files in the current directory<br/> /// - Specific extension: <c>.\*.js</c> - JavaScript files in the current directory<br/> /// - Complex pattern: <c>test*.log</c> - Log files starting with "test"<br/> /// - Recursive: <c>**\*.txt</c> - All txt files recursively in any subdirectory<br/> /// </para> /// /// <para type="description"> /// -Input <String><br/> /// File name or pattern to search for from pipeline input. Default is '*'.<br/> /// - <b>Aliases</b>: FullName<br/> /// - <b>Accepts pipeline input</b>: Yes<br/> /// - <b>Default</b>: "*"<br/> /// - <b>Features</b>:<br/> /// - Supports wildcards: <c>*</c> and <c>?</c> for pattern matching<br/> /// - Directory Navigation: <c>\</c> and <c>/</c> for path separators<br/> /// - Recursive pattern: <c>\**\</c> for recursive directory matching<br/> /// </para> /// /// <para type="description"> /// -Content <String><br/> /// Regular expression pattern to search within content.<br/> /// - <b>Aliases</b>: mc, matchcontent, regex, Pattern<br/> /// - <b>Position</b>: 1<br/> /// - <b>Default</b>: ".*"<br/> /// </para> /// /// <para type="description"> /// -RelativeBasePath <String><br/> /// Base path for resolving relative paths in output.<br/> /// - <b>Aliases</b>: base<br/> /// - <b>Position</b>: 2<br/> /// - <b>Default</b>: ".\\"<br/> /// </para> /// <para type="description"> /// -Category <String> = Pictures | Videos | Music | Documents | Spreadsheets | Presentations | Archives | Installers | Executables | Databases | DesignFiles | Ebooks | Subtitles | Fonts | EmailFiles | 3DModels | GameAssets | MedicalFiles | FinancialFiles | LegalFiles | SourceCode | Scripts | MarkupAndData | Configuration | Logs | TextFiles | WebFiles | MusicLyricsAndChords | CreativeWriting | Recipes | ResearchFiles /// SourceCode | Scripts | MarkupAndData | Documents | Spreadsheets | Presentations | TextFiles | LogFiles |WebFiles | Configuration | Ebooks | Subtitles | MusicLyricsAndChords | CreativeWriting | LegalAndContracts | RecipesAndCooking | ResearchAndAcademia /// <br/> /// Only output files belonging to selected categories.<br/> /// - <b>Aliases</b>: filetype<br/> /// </para> /// <summary> /// <para type="description">Only output files belonging to selected categories</para> /// </summary> /// /// <para type="description"> /// -MaxDegreeOfParallelism <Int32><br/> /// Maximum degree of parallelism for directory tasks.<br/> /// - <b>Aliases</b>: threads<br/> /// - <b>Default</b>: 0<br/> /// </para> /// /// <para type="description"> /// -TimeoutSeconds <Int32?><br/> /// Optional: cancellation timeout in seconds.<br/> /// - <b>Aliases</b>: maxseconds<br/> /// </para> /// /// <para type="description"> /// -AllDrives <SwitchParameter><br/> /// Search across all available drives.<br/> /// </para> /// /// <para type="description"> /// -Directory <SwitchParameter><br/> /// Search for directories only.<br/> /// - <b>Aliases</b>: dir<br/> /// </para> /// /// <para type="description"> /// -FilesAndDirectories <SwitchParameter><br/> /// Include both files and directories.<br/> /// - <b>Aliases</b>: both<br/> /// </para> /// /// <para type="description"> /// -PassThru <SwitchParameter><br/> /// Output matched items as objects.<br/> /// - <b>Aliases</b>: pt<br/> /// </para> /// /// <para type="description"> /// -IncludeAlternateFileStreams <SwitchParameter><br/> /// Include alternate data streams in search results.<br/> /// - <b>Aliases</b>: ads<br/> /// </para> /// /// <para type="description"> /// -NoRecurse <SwitchParameter><br/> /// Do not recurse into subdirectories.<br/> /// - <b>Aliases</b>: nr<br/> /// </para> /// /// <para type="description"> /// -FollowSymlinkAndJunctions <SwitchParameter><br/> /// Follow symlinks and junctions during directory traversal.<br/> /// - <b>Aliases</b>: symlinks, sl<br/> /// </para> /// /// <para type="description"> /// -IncludeOpticalDiskDrives <SwitchParameter><br/> /// Include optical disk drives.<br/> /// </para> /// /// <para type="description"> /// -SearchDrives <String[]><br/> /// Optional: search specific drives.<br/> /// - <b>Aliases</b>: drives<br/> /// - <b>Default</b>: Empty array<br/> /// </para> /// /// <para type="description"> /// -DriveLetter <Char[]><br/> /// Optional: search specific drives.<br/> /// - <b>Default</b>: Empty array<br/> /// </para> /// /// <para type="description"> /// -Root <String[]><br/> /// Optional: search specific directories.<br/> /// - <b>Default</b>: Empty array<br/> /// </para> /// /// <para type="description"> /// -IncludeNonTextFileMatching <SwitchParameter><br/> /// Include non-text files when searching file contents.<br/> /// - <b>Aliases</b>: binary<br/> /// </para> /// /// <para type="description"> /// -NoLinks <SwitchParameter><br/> /// Forces unattended mode and will not generate links.<br/> /// - <b>Aliases</b>: nl<br/> /// </para> /// /// <para type="description"> /// -CaseSensitivePattern <SwitchParameter><br/> /// Makes pattern matching case-sensitive. By default, pattern matching is case-insensitive.<br/> /// - <b>Aliases</b>: patternmatchcase, csp<br/> /// </para> /// /// <para type="description"> /// -CaseNameMatching <MatchCasing><br/> /// Gets or sets the case-sensitivity for files and directories.<br/> /// - <b>Aliases</b>: casing, CaseSearchMaskMatching<br/> /// - <b>Default</b>: PlatformDefault<br/> /// </para> /// /// <para type="description"> /// -SearchADSContent <SwitchParameter><br/> /// When set, performs content search within alternate data streams (ADS). When not set, outputs ADS file info without searching their content.<br/> /// - <b>Aliases</b>: sads<br/> /// </para> /// /// <para type="description"> /// -MaxRecursionDepth <Int32><br/> /// Maximum recursion depth for directory traversal. 0 means unlimited.<br/> /// - <b>Aliases</b>: md, depth, maxdepth<br/> /// - <b>Default</b>: 0<br/> /// </para> /// /// <para type="description"> /// -MaxFileSize <Int64><br/> /// Maximum file size in bytes to include in results. 0 means unlimited.<br/> /// - <b>Aliases</b>: maxlength, maxsize<br/> /// - <b>Default</b>: 0<br/> /// </para> /// /// <para type="description"> /// -MinFileSize <Int64><br/> /// Minimum file size in bytes to include in results. 0 means no minimum.<br/> /// - <b>Aliases</b>: minsize, minlength<br/> /// - <b>Default</b>: 0<br/> /// </para> /// /// <para type="description"> /// -ModifiedAfter <DateTime?><br/> /// Only include files modified after this date/time (UTC).<br/> /// - <b>Aliases</b>: ma, after<br/> /// </para> /// /// <para type="description"> /// -ModifiedBefore <DateTime?><br/> /// Only include files modified before this date/time (UTC).<br/> /// - <b>Aliases</b>: before, mb<br/> /// </para> /// /// <para type="description"> /// -AttributesToSkip <FileAttributes><br/> /// File attributes to skip (e.g., System, Hidden or None).<br/> /// - <b>Aliases</b>: skipattr<br/> /// - <b>Default</b>: System<br/> /// </para> /// /// <para type="description"> /// -Exclude <String[]><br/> /// Exclude files or directories matching these wildcard patterns (e.g., *.tmp, *\\bin\\*).<br/> /// - <b>Aliases</b>: skiplike<br/> /// - <b>Default</b>: "*\\.git\\*"<br/> /// </para> /// /// <example> /// <para>Find files containing a specific word</para> /// <para>Search for all files in the current directory and subdirectories that contain the word "translation".</para> /// <code> /// Find-Item -Pattern "translation" /// /// # Short form: /// l -mc translation /// </code> /// </example> /// /// <example> /// <para>Find JavaScript files with a version string</para> /// <para>Search for JavaScript files containing a version string in the format "Version == `x.y.z`".</para> /// <code> /// Find-Item "*.js" "Version == `"\d\d?\.\d\d?\.\d\d?`"" /// /// # Short form: /// l *.js "Version == `"\d\d?\.\d\d?\.\d\d?`"" /// </code> /// </example> /// /// <example> /// <para>List all directories</para> /// <para>Find all directories in the current directory and its subdirectories.</para> /// <code> /// Find-Item -Directory /// /// # Short form: /// l -dir /// </code> /// </example> /// /// <example> /// <para>Find XML files and pass objects</para> /// <para>Search for all .xml files and pass the results as objects through the pipeline.</para> /// <code> /// Find-Item ".\*.xml" -PassThru | % FullName /// /// # Short form: /// l *.xml -pt | % FullName /// </code> /// </example> /// /// <example> /// <para>Include alternate data streams</para> /// <para>Search for all files and include their alternate data streams in the results.</para> /// <code> /// Find-Item -IncludeAlternateFileStreams /// /// # Short form: /// l -ads /// </code> /// </example> /// /// <example> /// <para>Search across all drives</para> /// <para>Search for all PDF files across all available drives.</para> /// <code> /// Find-Item "*.pdf" -AllDrives /// /// # Short form: /// l *.pdf -alldrives /// </code> /// </example> /// /// <example> /// <para>Custom timeout and parallelism</para> /// <para>Search for log files with a 5-minute timeout and limited parallelism.</para> /// <code> /// Find-Item "*.log" -TimeoutSeconds 300 -MaxDegreeOfParallelism 4 /// /// # Short form: /// l *.log -maxseconds 300 -threads 4 /// </code> /// </example> /// /// <example> /// <para>Pipeline input</para> /// <para>Pass file paths from Get-ChildItem to search for files containing "error".</para> /// <code> /// Get-ChildItem -Path "C:\Logs" | Find-Item -Pattern "error" /// /// # Short form: /// ls C:\Logs | l -matchcontent "error" /// </code> /// </example> /// /// <example> /// <para>Limit recursion depth</para> /// <para>Search for text files but limit recursion to 2 directory levels.</para> /// <code> /// Find-Item "*.txt" -MaxRecursionDepth 2 /// /// # Short form: /// l *.txt -maxdepth 2 /// </code> /// </example> /// /// <example> /// <para>Filter by file size</para> /// <para>Find files larger than 1MB but smaller than 10MB.</para> /// <code> /// Find-Item -MinFileSize 1048576 -MaxFileSize 10485760 /// /// # Short form: /// l -minsize 1048576 -maxsize 10485760 /// </code> /// </example> /// /// <example> /// <para>Filter by modification date</para> /// <para>Find files modified after January 1, 2025.</para> /// <code> /// Find-Item -ModifiedAfter "2025-01-01" /// /// # Short form: /// l -after "2025-01-01" /// </code> /// </example> /// /// <example> /// <para>Exclude specific patterns</para> /// <para>Search for all files but exclude temporary files and bin directories.</para> /// <code> /// Find-Item -Exclude "*.tmp","*\bin\*" /// /// # Short form: /// l -skiplike "*.tmp","*\bin\*" /// </code> /// </example> /// /// <example> /// <para>Search specific drives</para> /// <para>Search for .docx files on C: and D: drives only.</para> /// <code> /// Find-Item "*.docx" -SearchDrives "C:\","D:\" /// /// # Short form: /// l *.docx -drives C:\, D:\ /// </code> /// </example> /// /// <example> /// <para>Case-sensitive content search</para> /// <para>Search for files containing "Error" (case-sensitive) in their content.</para> /// <code> /// Find-Item -Pattern "Error" -CaseSensitivePattern /// /// # Short form: /// l -matchcontent "Error" -patternmatchcase /// </code> /// </example> /// /// <example> /// <para>Search alternate data stream content</para> /// <para>Search for files with alternate data streams containing "secret".</para> /// <code> /// Find-Item -IncludeAlternateFileStreams -SearchADSContent -Pattern "secret" /// /// # Short form: /// </code> /// </example> /// /// <para type="link" uri="https://github.com/genXdev/GenXdev.FileSystem"> /// Online documentation: https://github.com/genXdev/GenXdev.FileSystem /// </para> [Cmdlet(VerbsCommon.Find, "Item", DefaultParameterSetName = "Default")] [Alias("l")] public partial class FindItem : PSCmdlet { /// <summary> /// <para type="description">File name or pattern to search for. Supports wildcards (*,?). Default is '*'</para> /// </summary> [Parameter(Position = 0, Mandatory = false, HelpMessage = "File name or pattern to search for. Default is '*'")] [Alias("like", "l", "Path", "Query", "SearchMask")] [ValidateNotNullOrEmpty()] [SupportsWildcards()] public string[] Name { get; set; } /// <summary> /// <para type="description">File name or pattern to search for from pipeline input. Default is '*'</para> /// </summary> [Parameter(Mandatory = false, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, HelpMessage = "File name or pattern to search for. Default is '*'")] [Alias("FullName")] [SupportsWildcards()] public string Input { get; set; } /// <summary> /// <para type="description">Regular expression pattern to search within file contents</para> /// </summary> [Parameter(Position = 1, Mandatory = false, ParameterSetName = "WithPattern", HelpMessage = "Regular expression pattern to search within content")] [Alias("mc", "matchcontent", "regex", "Pattern")] [ValidateNotNull()] [SupportsWildcards()] public string Content { get; set; } = ".*"; /// <summary> /// <para type="description">Base path for resolving relative paths in output</para> /// </summary> [Parameter(Position = 2, Mandatory = false, HelpMessage = "Base path for resolving relative paths in output")] [Alias("base")] [ValidateNotNullOrEmpty()] public string RelativeBasePath { get; set; } = ".\\"; /// <summary> /// <para type="description">Only output files belonging to selected categories</para> /// </summary> [Parameter(Mandatory = false)] [Alias("filetype")] [ValidateSet( "Pictures", "Videos", "Music", "Documents", "Spreadsheets", "Presentations", "Archives", "Installers", "Executables", "Databases", "DesignFiles", "Ebooks", "Subtitles", "Fonts", "EmailFiles", "3DModels", "GameAssets", "MedicalFiles", "FinancialFiles", "LegalFiles", "SourceCode", "Scripts", "MarkupAndData", "Configuration", "Logs", "TextFiles", "WebFiles", "MusicLyricsAndChords", "CreativeWriting", "Recipes", "ResearchFiles" )] public string[] Category { get; set; } /// <summary> /// <para type="description">Maximum degree of parallelism for directory tasks</para> /// </summary> [Parameter(Mandatory = false, HelpMessage = "Maximum degree of parallelism for directory tasks")] [Alias("threads")] public int MaxDegreeOfParallelism { get; set; } = 0; /// <summary> /// <para type="description">Optional: cancellation timeout in seconds</para> /// </summary> [Parameter(Mandatory = false, HelpMessage = "Optional: cancellation timeout in seconds")] [Alias("maxseconds")] public int? TimeoutSeconds { get; set; } /// <summary> /// <para type="description">Search across all available drives</para> /// </summary> [Parameter(Mandatory = false, HelpMessage = "Search across all available drives")] public SwitchParameter AllDrives { get; set; } /// <summary> /// <para type="description">Search for directories only</para> /// </summary> [Parameter(Mandatory = false, ParameterSetName = "DirectoriesOnly", HelpMessage = "Search for directories only")] [Alias("dir")] public SwitchParameter Directory { get; set; } /// <summary> /// <para type="description">Include both files and directories</para> /// </summary> [Parameter(Mandatory = false, ParameterSetName = "DirectoriesOnly", HelpMessage = "Include both files and directories")] [Alias("both")] public SwitchParameter FilesAndDirectories { get; set; } /// <summary> /// <para type="description">Output matched items as objects</para> /// </summary> [Parameter(Mandatory = false, HelpMessage = "Output matched items as objects")] [Alias("pt")] public SwitchParameter PassThru { get; set; } /// <summary> /// <para type="description">Include alternate data streams in search results /// </para> /// </summary> [Parameter(Mandatory = false, HelpMessage = "Include alternate data streams in search results")] [Alias("ads")] public SwitchParameter IncludeAlternateFileStreams { get; set; } /// <summary> /// <para type="description">Do not recurse into subdirectories</para> /// </summary> [Parameter(Mandatory = false, HelpMessage = "Do not recurse into subdirectories")] [Alias("nr")] public SwitchParameter NoRecurse { get; set; } /// <summary> /// <para type="description">Follow symlinks and junctions during directory /// traversal</para> /// </summary> [Parameter(Mandatory = false, HelpMessage = "Follow symlinks and junctions during directory traversal")] [Alias("symlinks", "sl")] public SwitchParameter FollowSymlinkAndJunctions { get; set; } /// <summary> /// <para type="description">Include optical disk drives</para> /// </summary> [Parameter(Mandatory = false, HelpMessage = "Include optical disk drives")] public SwitchParameter IncludeOpticalDiskDrives { get; set; } /// <summary> /// <para type="description">Optional: search specific drives</para> /// </summary> [Parameter(Mandatory = false, HelpMessage = "Optional: search specific drives")] [Alias("drives")] public string[] SearchDrives { get; set; } = Array.Empty<string>(); /// <summary> /// <para type="description">Optional: search specific drives</para> /// </summary> [Parameter(Mandatory = false, HelpMessage = "Optional: search specific drives")] public char[] DriveLetter { get; set; } = Array.Empty<char>(); /// <summary> /// <para type="description">Optional: search specific base folders combined with provided Names</para> /// </summary> [Parameter(Mandatory = false, HelpMessage = "Optional: search specific directories")] public string[] Root { get; set; } = Array.Empty<string>(); /// <summary> /// <para type="description">Include non-text files (binaries, images, etc.) /// when searching file contents</para> /// </summary> [Parameter(Mandatory = false, HelpMessage = "Include non-text files when searching file contents")] [Alias("binary")] public SwitchParameter IncludeNonTextFileMatching { get; set; } /// <summary> /// <para type="description">Forces unattended mode and will not generate /// links</para> /// </summary> [Parameter(Mandatory = false, HelpMessage = "Forces unattended mode and will not generate links")] [Alias("nl")] public SwitchParameter NoLinks { get; set; } /// <summary> /// <para type="description">Makes pattern matching case-sensitive. By default, /// pattern matching is case-insensitive.</para> /// </summary> [Parameter(Mandatory = false, HelpMessage = "Makes pattern matching case-sensitive. By default, pattern matching is case-insensitive.")] [Alias("patternmatchcase", "csp")] public SwitchParameter CaseSensitivePattern { get; set; } /// <summary>Gets or sets the case-sensitivity for files and directories. /// </summary> [Parameter(Mandatory = false, HelpMessage = "Gets or sets the case-sensitivity for files and directories")] [Alias("casing", "CaseSearchMaskMatching ")] public System.IO.MatchCasing CaseNameMatching { get; set; } = System.IO.MatchCasing.PlatformDefault; /// <summary> /// <para type="description"> /// When set, performs content search within alternate data streams (ADS). When /// not set, /// outputs ADS file info without searching their content. /// </para> /// </summary> [Parameter(Mandatory = false, HelpMessage = "When set, performs content search within alternate data streams (ADS). When not set, outputs ADS file info without searching their content.")] [Alias("sads")] public SwitchParameter SearchADSContent { get; set; } /// <summary> /// <para type="description">Maximum recursion depth for directory traversal. 0 /// means unlimited.</para> /// </summary> [Parameter(Mandatory = false, HelpMessage = "Maximum recursion depth for directory traversal. 0 means unlimited.")] [Alias("md", "depth", "maxdepth")] public int MaxRecursionDepth { get; set; } = 0; /// <summary> /// <para type="description">Maximum file size in bytes to include in results. 0 /// means unlimited.</para> /// </summary> [Parameter(Mandatory = false, HelpMessage = "Maximum file size in bytes to include in results. 0 means unlimited.")] [Alias("maxlength", "maxsize")] public long MaxFileSize { get; set; } = 0; /// <summary> /// <para type="description">Minimum file size in bytes to include in results. 0 /// means no minimum.</para> /// </summary> [Parameter(Mandatory = false, HelpMessage = "Minimum file size in bytes to include in results. 0 means no minimum.")] [Alias("minsize", "minlength")] public long MinFileSize { get; set; } = 0; /// <summary> /// <para type="description">Only include files modified after this date/time /// (UTC).</para> /// </summary> [Parameter(Mandatory = false, HelpMessage = "Only include files modified after this date/time (UTC).")] [Alias("ma", "after")] public DateTime? ModifiedAfter { get; set; } /// <summary> /// <para type="description">Only include files modified before this date/time /// (UTC).</para> /// </summary> [Parameter(Mandatory = false, HelpMessage = "Only include files modified before this date/time (UTC).")] [Alias("before", "mb")] public DateTime? ModifiedBefore { get; set; } /// <summary>Gets or sets the attributes to skip. The default is /// <c>FileAttributes.System</c>.</summary> [Parameter(Mandatory = false, HelpMessage = "File attributes to skip (e.g., System, Hidden or None).")] [Alias("skipattr")] public FileAttributes AttributesToSkip { get; set; } = FileAttributes.System; /// <summary> /// <para type="description">Exclude files or directories matching these /// wildcard patterns (e.g., *.tmp, *\bin\*).</para> /// </summary> [Parameter(Mandatory = false, HelpMessage = "Exclude files or directories matching these wildcard patterns (e.g., *.tmp, *\\bin\\*).")] [Alias("skiplike")] public string[] Exclude { get; set; } = new string[1] { "*\\.git\\*" }; // Cmdlet lifecycle methods protected override void BeginProcessing() { // set default parallelism if not provided by user MaxDegreeOfParallelism = MaxDegreeOfParallelism <= 0 ? GetCoreCount() : MaxDegreeOfParallelism; if (!String.IsNullOrWhiteSpace(Content) && Content != ".*") MaxDegreeOfParallelism *= 12; ThreadPool.GetMaxThreads(out this.oldMaxWorkerThread, out this.oldMaxCompletionPorts); ThreadPool.SetMaxThreads(MaxDegreeOfParallelism, MaxDegreeOfParallelism); // detect if running in unattended mode for output formatting UnattendedMode = NoLinks.IsPresent || UnattendedModeHelper.IsUnattendedMode(MyInvocation); // prepare dictionary to track visited nodes for loop prevention InitializeVisitedNodes(); // set up verbose logging based on user preference InitializeVerboseOutput(); // prepare wildcard matching for exclusions InitializeWildcardMatcher(); // set up exclusion patterns for files and directories InitializeExcludePatterns(); // determine current working directory for relative paths InitializeCurrentDirectory(); // resolve base path for relative output InitializeRelativeBaseDir(); // compile regex for content searching InitializePatternMatcher(); // configure buffering for large file processing InitializeBufferingConfiguration(); // set up cancellation with optional timeout InitializeCancellationToken(); // process each unique search mask provided if (Name != null && Name.Length > 0) { // loop through each mask foreach (var name in Name) { // check if mask already processed to avoid duplicates if (VisitedNodes.TryAdd("start;" + name, true)) { // log processing of mask if verbose enabled if (UseVerboseOutput) { VerboseQueue.Enqueue($"Processing name: {name}"); } // prepare search starting point InitializeSearchDirectory(name); } else if (UseVerboseOutput) { // log skipping duplicate mask VerboseQueue.Enqueue($"Skipping duplicate name: {name}"); } } } } protected override void ProcessRecord() { if (string.IsNullOrEmpty(Input)) return; // add to visited if new and initialize search if (VisitedNodes.TryAdd("start;" + Input, true)) { InitializeSearchDirectory(Input); } else if (UseVerboseOutput) { // log skipping duplicate mask VerboseQueue.Enqueue($"Skipping duplicate name: {Input}"); } } protected override void EndProcessing() { // check for no params if (DirQueue.Count == 0) { if (UseVerboseOutput) { // log skipping duplicate mask VerboseQueue.Enqueue($"No input, adding current directory to the queue: {CurrentDirectory}\\"); } InitializeSearchDirectory(CurrentDirectory + "\\"); } // allow new workers to be created isStarted = true; // run the search tasks ProcessSearchTasks(); // clear all queues EmptyQueues(); // create completion progress record var completeRecord = new ProgressRecord(0, "FastFileSearch", "Completed") { // set completion percentage PercentComplete = 100, // mark as completed RecordType = ProgressRecordType.Completed }; // output completion progress WriteProgress(completeRecord); // restore configuration ThreadPool.SetMaxThreads(this.oldMaxWorkerThread, this.oldMaxCompletionPorts); // clean up cancellation source cts?.Dispose(); } protected override void StopProcessing() { // trigger cancellation cts?.Cancel(); } } |