Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to stop Get-Content -wait after I find a particular line in the text file using powershell? Exit a pipeline on demand

I am using this below script in Powershell (Version is 5.1):

Get-Content -Path path\to\text\file\to\be\read.txt -Wait

Now this continues to read even after the file is getting no update. How can I stop after I find particular line in the text file? Is there any other way to stop this on condition?

like image 473
Ravi Sharma Avatar asked Jan 02 '23 23:01

Ravi Sharma


2 Answers

tl;dr

do {
  Get-Content -Path path\to\text\file\to\be\read.txt -Wait | ForEach-Object {
    # Pass the input line through.
    $_
    # Exit once the string of interest is found.
    if ($_ -match 'patternOfInterest') { break } 
  }
} while ($false) # dummy loop so that `break` can be used.

# NOTE: Without the dummy `do` loop, this code would not be reached.
'Line of interest found.'

Note that the -match operator performs substring matching using regexes (about_Regular_Expressions).

Read on for why the dummy do loop is needed.


Paxz's answer is on the right track, but it tries to use break (in isolation) to exit the pipeline, which (typically) terminates the whole script (if you submit the pipeline as a single command interactively, you may not notice).

break / continue are designed to exit / continue loops (for, foreach, do, while, and switch statements[1]), not pipelines.

As of PowerShell [Core] 7.2.2, there is no direct way to exit a pipeline prematurely, though adding this feature is the subject of this long-standing feature request on GitHub; currently, only direct use of Select-Object with -First can exit a pipeline prematurely, through use of non-public exception; while return can be used in a ForEach-Object script block, it only exits the current script-block invocation while continuing to process additional pipeline input.

If you use break or continue, PowerShell will look for any enclosing loop and if there is none, the script as a whole exits.

However, if you wrap a dummy loop around your pipeline[2], as shown above - whose sole purpose is to provide something for break to break out of - execution continues, as (usually) desired.

Caveat:

break / continue (and throw) have an important side effect: they do not give the other commands participating in the pipeline a chance to exit normally; that is, their end blocks / EndProcessing() methods are not called, which can be problematic in two respects:

  • Cmdlets that need to clean up (dispose of) temporarily held resources do not get to clean up.

    • Note that, regrettably, this inability to clean up also applies to use of Select-Object -First with advanced PowerShell functions/scripts (but not with (binary) cmdlets, which can ensure cleanup by implementing the System.IDisposable interface), as discussed in GitHub issue #7930; GitHub PR #9900 aims to address that introducing a dedicated cleanup block.
  • Aggregating cmdlets - those that of necessity collect all input before being able to produce output - never get to produce output; here's a simple example:

PS> do { 5..1 | % { $_; if ($_ -eq 3) { break } } | Sort-Object } while ($false)
# !! NO OUTPUT, because Sort-Object's EndProcessing() method was never called.

[1] When you examine a single value with a switch statement, break exits the statement, as you would expect. If an array (a collection) of values is being processed, break too exits the statement immediately, without processing further elements in the array; continue, by contrast, continues processing with the next element.

[2] Another option is to use throw to generate a script-terminating error that you catch by enclosing the pipeline in a try / catch statement, as shown in this answer. However, this has the side effect of recording an entry in the automatic $Error collection, even though conceptually no actual error occurred.

like image 131
mklement0 Avatar answered Jan 04 '23 13:01

mklement0


You can pipe the output and use foreach to check each line, if the line equals a certain string you can use break to stop the command:

Get-Content path\to\text\file\to\be\read.txt -wait | % {$_ ; if($_ -eq "yourkeyword") {break}}

For example, if you run the command above and do the following in another shell:

"yourkeyword" | Out-File path\to\text\file\to\be\read.txt -Append

The Get-Content displays the new line and stops.


Explanation:

| % - foreach line that is in the file or gets added to the file while the function runs

$_; - first write out the line of the foreach then do another command

if($_ -eq "yourkeyword") {break} - if the line of the foreach is equal to the keyword/line you want, stop the Get-Content

like image 28
Paxz Avatar answered Jan 04 '23 13:01

Paxz