PowerShell to monitor a text file - powershell

I have a PowerShell script running on a remote machine. I have it writing data to a text file as it completes its work. Once it's done it writes a specific line.
I have this in the local PowerShell script to monitor that file on the remote machine:
Get-Content -Path $Path -Tail 0 -Wait
It is working great, but how do I tell it to stop monitoring once that specific line is reached?
I tired putting it into a do while loop, but it never releases to complete the do while.
Here is a link to a simpler version of what I am asking:
How to monitor a text file in realtime
The first answer is good, but I don't want to just look for a certain line. I want to write them all VIA Write-Host till that phrase then break from Get-Content and continue with the remaining parts of the script.
Here is what I finally ended up with. It is not pretty due to the way I exited the ForEach-Object.
Get-Content -Path $path -Tail 0 -wait | ForEach-Object{if($_ -match $word){write-host "- $_" ;cjklnsrvf } else {write-host "- $_"} }
I used a Try and Catch for the cjklnsrvf in the if statement above. This is done because ForEach-Object cannot use the break or continue statements. It seems that when piping a ForEach loop it is turned into (alias) the ForEach-Object cmdlet. The ForEach-Object cmdlet doesn't use the break and continue commands like a foreach loop.
If you use a break in a ForEach-Object it will immediately exit the whole script. There was one guy on one site that brought up loop death by garbage, and it indeed does work here as well.

UPDATE: Here is what I finally ended up with. It is not pretty due to the way I exited the foreach-object.
Get-Content -Path $path -Tail 0 -wait | ForEach-Object{if($_ -match $word){write-host "- $_" ;cjklnsrvf } else {write-host "- $_"} }
I used a Try and Catch for the cjklnsrvf in the if statement above. This is done because ForEach-Object cannot use the break or continue statements. It seems that when piping a ForEach loop it is turned into(alias) the ForEach-Object cmdlet. The ForEach-Object cmdlet doesn't use the break and continue commands like a foreach loop. If you use a break in a ForEach-Object it will immediately exit the whole script. There was one guy on one site that brought up loop death by garbage and it indeed does work here as well.

Get-Content $path -Tail 0 -Wait | foreach { if ($_ -eq "Specific Line") { Write-Output $_ ; break } }
Or just the break, obviously, if you have no use of the output.

Do {
$content = get-content $path -tail 0 -ea 00 | where {$_ -like "*string found*"}
Sleep -milliseconds 1000
} until($content)

Invoke-Command -ComputerName xxx -Credential $cred -ScriptBlock {if(test-path 'c:\1.txt'){cat 'c:\1.txt'}else{'file not exist'}}

Related

Unable to use the "ForEach-Object -Parallel" in a directory with a name containing "["

Using the ForEach-Object -Parallel cmdlet in a directory with a name containing "[" will return a WildcardPatternException. Remove -Parallel, it will run successfully.
I created a few directories and ran the following commands.1..5 | ForEach-Object -Parallel {Write-Host $_}A difference of a return for each ran directory is as follows.
PS D:\[example> 1..5 | ForEach-Object -Parallel {Write-Host $_}
WildcardPatternException will be returned.
PS D:\[]example> 1..5 | ForEach-Object -Parallel {Write-Host $_}
PS D:\[ex]ample> 1..5 | ForEach-Object -Parallel {Write-Host $_}
ItemNotFoundException will be returned.
PS D:\[e]xample> 1..5 | ForEach-Object -Parallel {Write-Host $_}
PS D:\]example> 1..5 | ForEach-Object -Parallel {Write-Host $_}
Ran successfully and 5 values are returned.
Is there a way to resolve this without renaming directories?
As mentioned in the comments, this is a bug.
ForEach-Object -Parallel works by offloading execution of the scriptblock to multiple background runspaces via something called a TaskPool.
In order to mimic the callers environment, PowerShell configures the background runspaces to resemble the default runspace, including setting the current provider lotation to whatever the callers is.
Unfortunately, the internal API for setting the current location defaults to expand wildcards, the equivalent of defaulting to:
Set-Location -Path 'D:\[]example'
instead of
Set-Location -LiteralPath 'D:\[]example'
This has now been patched (both for ForEach-Object -Parallel and Start-PSThreadJob), expect both to ship with version 7.1 later this year

Setting Powershell output to a variable

So I'm working on a script that will have a variable set to a file path, then it will Get-ChildItem of that path and if these items are over a certain size then it will print the output of that with the name of the file and the file size.
Get-ChildItem $file | ? {$_.Length -gt 1mb} | ForEach-Object {Write-Host "Users:" $_.name "have Outlook Data Files larger than 8gb, with a total of" ("{0:N2}" -f($_.length/1mb)) "mb"}
I am trying to assign this output, to a variable so I can utilize the second command and send this output in an email to myself. Unless there is a better way to accomplish this.
$content = gci -Recurse -File | ? { $_.Length -gt 40000 }
Include $content as the body of your email.
As the previous comments have pointed out, it seems like your primary problem might be a misunderstanding about how the Write-* cmdlets work in PowerShell. Write-Host outputs directly to the host/console, bypassing the normal PowerShell output streams. This can be illustrated quickly by running the following commands in a PowerShell session:
$MyVariable1 = Write-Host "Hello, World!"
$MyVariable2 = Write-Output "Hello, World!"
$MyVariable1
$MyVariable2
If you run the above, you'll find that $MyVariable1 has no value assigned (and you could actually test it for that with something like $null -eq $MyVariable1) but $MyVariable2 will have the value 'Hello, World!'.
For your example to work to get your output into a variable you would need to run something like the following:
$LargeFiles = Get-ChildItem $file | ? {$_.Length -gt 1mb} | ForEach-Object {Write-Output "Users:" $_.name "have Outlook Data Files larger than 8gb, with a total of" ("{0:N2}" -f($_.length/1mb)) "mb"}
To learn more about PowerShell output streams you might also want to read the about_redirection article.

How do I output each processing item to file?

Get-AppXPackage -AllUsers | Foreach {Add-AppxPackage -DisableDevelopmentMode -Register "$($_.InstallLocation)\AppXManifest.xml"} | Out-File -path $env:temp\AppXPackage.log
This performs the task that I want it to perform. However, the log file is blank.
I would like for the log file to contain both the errors, and log the individual operations.
I'm not familiar with that particular cmdlet, you could probably try redirecting other output streams (so not stdout) to the log file, and it might work. at the very worst you can do something like this:
Foreach-Object {
try {
Add-AppxPackage ... -ErrorAction Stop
"$_ done" > success.log
} catch {
$_ > errors.log
}
}
The Add-AppxPackagedoesn't return anything, neither does it support -PassThru argument. However, you could push the $_ object to the pipe from the foreach block:
Get-AppXPackage -AllUsers |
Foreach {Add-AppxPackage -DisableDevelopmentMode -Register
"$($_.InstallLocation)\AppXManifest.xml"; $_ } |
Out-File -path $env:temp\AppXPackage.log
As the other 2 answers mentioned, that cmdlet doesn't seem to give any output. Which leaves you with either manual logging or redirection. I just wanted to expand a bit on the other two to give you a couple options.
For redirection, you can do something like this:
.\my_script *>> myfile.log #appen all streams (output and errors) to a file
.\my_script >> output.log 2>> errors.log #send output to one file and error stream to another
For normal logging, take a look at $error, which will contain all the errors in your session. You could start your script with $error.clear(), manually log your output you want since the cmdlet doesn't have output and then end with $error | out-file errors.log

Tail a log file and if match trigger an actrion

I am currently using the following line of code to return the most recent line of a log via PowerShell
Get-Content -Path C:\folder\thisisalog.log -Tail 1 -Wait |
Where {$_ -match "Remote_http"}
This works correctly and will write to the console each time a log that matches "Remote_http" is logged.
However what I would like to do is run another script when this is returned.
So far I have tried to add to a variable and check if it is null with no luck and I have tried using if statements with no success.
Trying both of these the script runs indefinitely with no output to console or triigers.
I think it may be something to do with -Wait which is causing the issue.
just do it
Get-Content -Path C:\folder\thisisalog.log -Tail 1 -Wait | % {if ($_ -match "Remote_http") {write-host "run code here"}}
or directly into your where
Get-Content -Path C:\folder\thisisalog.log -Tail 1 -Wait | where {if ($_ -match "Remote_http") {write-host "run code here"}}

Count number of scripts running and wait for them to finish

I'm looking for the best way to count the number of PowerShell scripts that are currently running.
I run .ps1 scripts from windows batch files. The script I am working on now is launched when a particular email is received from a client - but I want this script to first of all check that no other scripts are busy running at the moment, and if they are it must wait for them to finish before it continues.
I'm sure there are a few ways to go about this, but what would be the safest? I am still learning.
If it is possible to move away from batch files to launch PowerShell then I would suggest using Start-Process to launch your scripts. This will allow you to wait for your processes to exit using where-object and Measure-Object to filter the scripts that have not yet completed.
So your script might look something like this:
# create a loop
foreach ($item in $reasontoloop) {
$arguments = "define script names and arguments"
# Start the powershell script
$procs += Start-Process powershell -PassThru -argumentlist $arguments
}
Write-Host -message "Waiting for Processes to complete"
while( $procs | Where-Object { $_.hasExited -eq $false } )
{
# Display progress
$measureInfo = $procs | Where-Object { $_.hasExited -eq $true } | Measure-Object
write-host "$($measureInfo.count) of $($procs.Length) still running"
Start-Sleep 1
}
Write-Host -message "Processes complete"
If you are simply interested in the number of PowerShell instances executing then the following one liner using Get-Process will help.
#(Get-Process | where-object {$_.ProcessName -like 'powershell'}).count