Home > Net >  How to create an elseif condition in a script with Runspaces
How to create an elseif condition in a script with Runspaces

Time:07-21

In the original script, I was attempting to search for a string in a text file in a running log. It worked fine however, since there is a -wait parameter in the loop, it wasn't easy to find a solution that would allow for the same script to search over multiple text files. Since then the following script was introduced to me that incorporates Runspaces:

using namespace System.Management.Automation.Runspaces
using namespace System.Threading

# get the log files here
$LogGroup = ('C:\log 0.txt', 'C:\Log 1.txt', 'C:\Log 2.txt')
# this help us write to the main log file in a thread safe manner
$lock     = [SemaphoreSlim]::new(1, 1)

# define the logic used for each thread, this is very similar to the
# initial script except for the use of the SemaphoreSlim
$action = {
    param($path)

    $PSDefaultParameterValues = @{ "Get-Date:format" = "yyyy-MM-dd HH:mm:ss" }
    Get-Content $path -Tail 1 -Wait | ForEach-Object {
        if($_ -match 'down') {
            # can I write to this file?
            $lock.Wait()
            try {
                Write-Host "Down: $_ - $path" -ForegroundColor Green
                Add-Content "path\to\mainLog.txt" -Value "$(Get-Date) Down: $_ - $path"
            }
            finally {
                # release the lock so other threads can write to the file
                $null = $lock.Release()
            }
        }
    }
}

try {
    $iss = [initialsessionstate]::CreateDefault2()
    $iss.Variables.Add([SessionStateVariableEntry]::new('lock', $lock, $null))
    $rspool = [runspacefactory]::CreateRunspacePool(1, $LogGroup.Count, $iss, $Host)
    $rspool.ApartmentState = [ApartmentState]::STA
    $rspool.ThreadOptions  = [PSThreadOptions]::UseNewThread
    $rspool.Open()

    $res = foreach($path in $LogGroup) {
        $ps = [powershell]::Create($iss).AddScript($action).AddArgument($path)
        $ps.RunspacePool = $rspool
        @{
            Instance    = $ps
            AsyncResult = $ps.BeginInvoke()
        }
    }

    # block the main thread
    do {
        $id = [WaitHandle]::WaitAny($res.AsyncResult.AsyncWaitHandle, 200)
    }
    while($id -eq [WaitHandle]::WaitTimeout)
}
finally {
    # clean all the runspaces
    $res.Instance.ForEach('Dispose')
    $rspool.ForEach('Dispose')
}

The Runspaces allow for additional threads allowing for multitasking but I am not very skilled and I need help adding an elseif clause after an if statement. But my attempts were rewarded with the following error:

cmdlet ForEach-Object at command pipeline position 2 
Supply values for the following parameters: 
Process[0]:

Here’s the best I could come up with so far:

$action = { 
    param($path) 
    
    $PSDefaultParameterValues = @{ "Get-Date:format" = "yyyy-MM-dd HH:mm:ss" } 
    
    $lock.Wait() 
    # can I write to this file? 
    try { 
        Get-Content $path -Tail 1 -Wait | ForEach-Object 
            if($_ -match 'down) { 
                Write-Host "Down: $_ - $path" -ForegroundColor Red 
                Add-Content "C:\ log_down.txt" -Value "$(Get-Date) Down: $_ - $path" 
            } 
            elseif($_ -match 'up') { 
                Write-Host "Down: $_ - $path" -ForegroundColor Green 
                Add-Content "C:\ log_up.txt" -Value "$(Get-Date) up: $_ - $path" 
            } 
            $lock.Wait() 
        }
        
    finally { 
        # release the lock so other threads can write to the file 
        $null = $lock.Release() 
    } 
}

Thanks in advance for any help!

CodePudding user response:

Here is the only change you need to do, below code only addresses the $action Script Block. Rest of the code should remain the same.

Make sure you're using the Full Paths of the logs.

$action = { 
    param($path) 
    
    $PSDefaultParameterValues = @{ "Get-Date:format" = "yyyy-MM-dd HH:mm:ss" } 
    Get-Content $path -Tail 1 -Wait | ForEach-Object {
        # wait to enter the SemaphoreSlim
        $lock.Wait()
        try {
            if($_ -match 'down') { 
                Write-Host "Down: $_ - $path" -ForegroundColor Red 
                Add-Content "C:\log_down.txt" -Value "$(Get-Date) Down: $_ - $path"
            }    
            elseif($_ -match 'up') { 
                Write-Host "Up: $_ - $path" -ForegroundColor Green 
                Add-Content "C:\log_up.txt" -Value "$(Get-Date) up: $_ - $path"
            }
            # more conditions can go here
        }
        finally {
            # release the lock so other threads can write to the file 
            $null = $lock.Release() 
        }
    }
}

After closer look at your attempt, it seems almost right:

  1. Missing an opening { after ForEach-Object.
  2. Missing a closing } before the finally block.
  3. .Wait() should be inside the loop instead of outside.
  • Related