简体   繁体   中英

How to fix/speed up powershell script?

I know there is code review for 'speed up' exists, but I also need to 'fix' problem of my script.

I migrated from Command Prompt( .bat ) to Powershell( .ps1 ) because I think Command Prompt is hard to make script for complex things. I've heard that Powershell may have some overhead than Command Prompt, but if it is fast enough, I don't care.

Here is link for two files, sdn.old.bat and sdn.new.ps1 . I put them in paste site because those are long enough.

paste.gg

Here is my problem.

This part takes a very long time to run.

$logs_loc = @(
    "$Env:LocalAppdata"
    "$Env:Appdata"
)
ForEach ($item in $logs_loc) {
    Get-ChildItem -Path "$item\*" -Recurse -Force -Include *.log *.log.txt | Remove-Item -Force
}

It takes ~5 seconds and I don't know why. This also doesn't do anything. This code should remove all *.log or *.log.txt files under %Appdata% and %LocalAppdata% , but it don't delete anything. I tested with test file randomly placed blank *.log and *.log.txt but they remain after run.

I haven't tested other part of my script, so there may another problem exists...


TL;DR

  1. I don't know why my code doesn't work but also takes too much time for running.
  2. Is there anything that can 'improve' the speed?

Otter's helpful answer already explains the issue with your current code, the -Include parameter takes string[] ( string array ) as argument, if you want to pass multiple filters to the parameter you need to separate them by a comma , . See about_Arrays for details.

As for improving the efficiency of your code, you would need to make .NET API calls to IO.Directory as zett42 points out in a comment or IO.DirectoryInfo , both options are valid however the latter outputs IO.FileInfo instead of strings. For handling the folder recursion you can use a Queue<T> instance :

$env:LocalAppdata, $env:Appdata | & {
    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeline)]
        [string] $Path
    )

    begin { $queue = [Collections.Generic.Queue[IO.DirectoryInfo]]::new() }
    process { $queue.Enqueue($Path) }
    end {
        while($queue.Count) {
            $dir = $queue.Dequeue()
            foreach($filter in '*.log', '*.log.txt') {
                $dir.EnumerateFiles($filter)
            }
            foreach($i in $dir.EnumerateDirectories()) {
                $queue.Enqueue($i)
            }
        }
    }
} -ErrorAction SilentlyContinue | Remove-Item -Force

In .NET Core / PowerShell Core 7+ , this task simplifies a lot thanks to the EnumerationOptions Class which allows us to Ignore Inaccessible files and folders:

# IgnoreInaccessible is set to `$true` by Defaut.

$enum = [IO.EnumerationOptions]@{
    RecurseSubdirectories = $true
    AttributesToSkip      = 2, 4, 1024, 512
}

$env:LocalAppdata, $env:Appdata | ForEach-Object {
    foreach($filter in '*.log', '*.log.txt') {
        [IO.Directory]::EnumerateFiles($_, $filter, $enum)
    }
} | Remove-Item -Force

See this answer for details on AttributesToSkip .

The issue is that your -include parameters is interpreting *.log *.log.txt as a single pattern, but we want it to match either pattern so just separate them by a , , so *.log, *.log.txt .

$logs_loc = @(
    "$Env:LocalAppdata"
    "$Env:Appdata"
)
ForEach ($item in $logs_loc) {
    Get-ChildItem -Path "$item\*" -Recurse -Force -Include *.log, *.log.txt | Remove-Item -Force
}

As for speed, this seems like an operation you wont be performing often so im not sure its worth investing much time into making it faster than it is, mine takes 10 seconds. I suggest asking Code Review for that bit!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM