1

I have a PowerShell script that checks if a file is present in a folder. The problem is: This script works as it should, but it's very slowly. I must check 10K Pcs for a statistic / day. I want to use Invoke-Command, but I can't use it because not all clients have enabled WinRM. Is it possible to make this script multithread without WinRM?

Here is my code:

function Show-Menu {
    param (
        [string]$Title = 'Debug'
    )
    cls
    Write-Host "================ $Title ================"

    Write-Host "Ziel Clients werden in der Datei C:\temp\srv.txt angegeben!" -ForegroundColor Red
    Write-Host "1: Detailansicht alle Clients" -ForegroundColor Green
    Write-Host "2: Today Crash" -ForegroundColor Green
    Write-Host "3: Detailansich einzelner Client" -ForegroundColor Green
    Write-Host "Q: 'Q' zum beenden."
    Write-Host "Script wird ausgefuehrt als:"
    whoami
}
do {
    Show-Menu
    $input = Read-Host "Nummer angeben"
    switch ($input) {
        '1' {
            cls
            Write-Host "Detailansicht alle Clients" 
            $computers = Get-Content C:\temp\srv.txt

            foreach ($computer in $computers) {

                Write-Host -foregroundcolor "green" "Verarbeite $computer..." 
                if ( ! (Test-Connection $computer -Count 1 -Quiet)) {
                    Write-Host -foregroundcolor "red" "$computer ist offline" 
                    continue
                }

                $path = Test-Path "\\$computer\c$\Program Files\Oracle\Runtime\BIN\ifrun60_*" -Include *dump* 
                Get-Item "\\$computer\c$\Program Files\Oracle\Runtime\BIN\ifrun60_*"
                If ($path -eq $true ) { Write-Host $computer 'Dumps are present' }

                Else { Write-Host $computer 'Dumps are not present' } 
                pause
            }

        } 
        '2' {
            cls
            Write-Host "Today Crash" 
            $computers = Get-Content C:\temp\srv.txt

            foreach ($computer in $computers) {
                Write-Host -foregroundcolor "green" "Verarbeite $computer..." 
                if ( ! (Test-Connection $computer -Count 1 -Quiet)) {
                    Write-Host -foregroundcolor "red" "$computer ist offline" 
                    continue
                }

                $result = Get-ChildItem -Path "\\$computer\c$\Program Files\Oracle\Runtime\BIN\ifrun60_*" | Where-Object { $_.LastWriteTime -ge (Get-Date).Date }
            }
            $result | Out-GridView
        }
        '3' {
            cls
            Write-Host "Detailansich einzelner Client" 
            $computer = Read-Host -Prompt 'Client angeben'
            $result = Get-ChildItem -Path "\\$computer\c$\Program Files\Oracle\Runtime\BIN\ifrun60_*"
            $result | Out-GridView
        }
    }
}
until ($input -eq 'q')
1
  • Hi, You can use Background jobs as the easiest solution. start-job should be Your friend :) Commented Jun 12, 2020 at 9:30

1 Answer 1

3

While background jobs - started via Start-Job - do permit running in parallel, they run in child processes, and are therefore both resource-intensive and slow; furthermore, you cannot throttle their use, i.e. you cannot (directly) control how many child process at most are permitted to run simultaneously

Thread jobs, by contrast, run as threads in-process and therefore require fewer resources and are much faster than child-process-based background jobs; furthermore, throttling (limiting the number of threads permitted to run simultaneously) is supported.

Thread jobs are started via the Start-ThreadJob cmdlet, which comes with PowerShell [Core] v6+ and in Windows PowerShell can be installed on demand with, e.g., Install-Module ThreadJob -Scope CurrentUser.

You simply call Start-ThreadJob instead of Start-Job, and use the standard *-Job cmdlets to manage such thread jobs - the same way you'd manage a Start-Job-launched background job.

In the simplest case, you can simply wait for all threads to complete:

# PowerShell [Core] 6+
# Windows PowerShell with the ThreadJob module installed.
Get-Content C:\temp\srv.txt | ForEach-Object {
  $computer = $_
  Start-ThreadJob { # create a thread job and output an object representing it
    Write-Host -ForegroundColor Green "Processing $using:computer..."
    # ...
    Get-Item "\\$using:computer\c$\Program Files\Oracle\Runtime\BIN\ifrun60_*"
    # ...
  }
} | Receive-Job -Wait -AutoRemoveJob
  • Note the use of the $using: scope specifier, which is needed to access the caller's $computer value; this requirement applies equally to background jobs, thread jobs, and remoting.

  • By default, up to 5 threads are allowed to run simultaneously; you can use the -ThrottleLimit parameter to modify this value, but note that increasing this value beyond what your hardware can support can actually slow things down.

  • Output sequencing isn't guaranteed; that is, the outputs aren't guaranteed to correspond to the order in which the computer names were specified.


In PowerShell 7+, there's an even easier way to run threads in parallel, via ForEach-Object's
-Parallel
parameter:

# PowerShell 7+
Get-Content C:\temp\srv.txt | ForEach-Object -Parallel {
  Write-Host -foregroundcolor Green "Processing $_..."
  # ...
  Get-Item "\\$_\c$\Program Files\Oracle\Runtime\BIN\ifrun60_*"
  # ...
}

The comments above regarding output sequencing and throttling apply equally here, but note that the computer names are now provided as input via the pipeline, so the usual automatic $_ variable can be used inside the thread script block.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.