Overwrite PowerShell output strings onto the same line - powershell

I have a piece of PS code which takes the 7-Zip extraction output and filters it down so only percentage "%" progress update lines get printed. I've managed to reduce it down to just the percentage outputs:
& $7ZipPath "x" $filePath "-o$extractionPath" "-aos" "-bsp1" | out-string -stream | Select-String -Pattern "\d{1,3}%" -AllMatches | ForEach-Object { $_.Matches.Value } | Write-Host -NoNewLine
At the moment the console output looks like this:
0%1%5%9%14%17%20%23%26%31%37%43%46%48%50%52%54%56%59%61%63%65%67%70%72%74%76%78%80%81%82%83%85%86%87%89%90%91%92%94%95%96%97%98%99%
Is there a way of keeping these outputs in the same place, on the same line, making them just overwrite each other? It's tricky because the output is being piped from the 7-Zip application. I'm afraid I can't use Expand-Archive as I am dealing with .7z files
Many thanks!

You could use the .Net System.Console class:
[System.Console]::SetCursorPosition(0, [System.Console]::CursorTop)
So your code would have to be:
& $7ZipPath "x" $filePath "-o$extractionPath" "-aos" "-bsp1" | out-string -stream | Select-String -Pattern "\d{1,3}%" -AllMatches | ForEach-Object { $_.Matches.Value } | foreach {
[System.Console]::SetCursorPosition(0, [System.Console]::CursorTop)
Write-Host $_ -NoNewLine
}
Note: As long as the next output is equal or greater length, which is true in your case, this is all you need. Otherwise you would have to clear the last output first.

marsze's helpful answer works well, but there's a simpler alternative that uses a CR character ("`r") to reset the cursor position to the start of the line.
Here's a simple demonstration that prints the numbers 1 through 10 on the same line:
1..10 | ForEach-Object { Write-Host -NoNewline "`r$_"; Start-Sleep -Milliseconds 100 }
[Console]::Write(...) instead of Write-Host -NoNewline ... works too, as Bacon Bits points out.
The same constraint applies, however: if previous output lines happened to be longer, the extra characters linger.
To solve this problem too, you must pad any output line to the length of the console window's buffer width:
'loooooooong', 'meeedium', 'short' | ForEach-Object {
Write-Host -NoNewline ("`r{0,-$([console]::BufferWidth)}" -f $_)
Start-Sleep -Milliseconds 500
}

Related

Powershell - Count lines in files excluding/including certain lines

I have the script below that will count ALL the lines of all my files in the directory and sub-directory.
Works fine and creates the output file fine.
The issue I'm having now, is that all the files have comments and executable lines in them and I need to separate the two.
I have to count all the lines that have an Asterisk in position 7. These are comments. A simple calculation of total lines minus comment lines will provide the last artifact I need which is executable lines.
Can someone help out with altering the below code to just count Asterisk in position 7.
Thank you in advance,
-Ron
$path='C:\'
$outputFile='C:\Output.csv'
$include='*.cbl'
$exclude=''
param([string]$path, [string]$outputFile, [string]$include, [string]$exclude)
Clear-Host
Get-ChildItem -re -in $include -ex $exclude $path |
Foreach-Object { Write-Host "Counting '$($_.Name)'"
$fileStats = Get-Content $_.FullName | Measure-Object -line
$linesInFile = $fileStats.Lines
"$_,$linesInFile" } | Out-File $outputFile -encoding "UTF8"
Write-Host "Complete"
I would do something like this
$linesInFile = 0
switch -Regex -File $_.FullName {
'^.{6}\*' { <# don't count this line #> }
default { $linesInFile++ }
}
This also should be faster than using Get-Content.
P.S. Also adding -File to the Get-ChildItem helps to eliminate processing directories.

Powershell - replace line in .txt if condition is met

i'm quite new to scripting (few weeks) and would be happy about your help.
I've a log-file (.txt) which needs to be changed.
The content is always the same:
random text
random text
successfull
error
random text
random text
random text
error
...
I would like to remove the line containing the word "error", but only if the line above contains the word "successfull".
So far I managed to get all the matching strings out of the File and am able to replace them, but I lose the rest of the text in the process:
get-content "D:\test.txt" | select-string -pattern "error" -context 1,0 | Where-Object {"$_" -match "successfull" } | %{$_ -replace "error.*"} | Out-File "D:\result.txt"
I would really appreciate your help here.
You can use some conditional logic (if statements) to achieve the goal:
$successful = $false
Get-Content d:\test.txt | Foreach-Object {
if ($_ -match "successfull") {
$successful = $true
$_
}
elseif ($_ -match "error" -and $successful) {
$successful = $false
}
else {
$_
$successful = $false
}
}
Since we are piping the Get-Content result into Foreach-Object, $_ becomes the current line being processed (each line is processed one by one). If a line contains successfull, then we mark $successful as $true and still output that line ($_). If the line contains error, then we will only output it if $successful is $false. Anytime we reach a line that does not contain succcesfull, we mark $successful as $false.
No deletion is actually occurring as it is merely not displaying error lines when the conditions are met.

Powershell remove lines containing the incorrect number of words

Can this be done in easily or at all in powershell?
How would one remove all lines from "test.txt" that do not contain exactly 24 words
This is not too hard in PowerShell.
Something like below should do it:
# read the file and use Where-Object to capture only those lines that have 24 words exactly.
# the regex -split uses '\W+', meaning to split each line on (at least one) Non-Word character.
$result = Get-Content -Path 'D:\test.txt' | Where-Object { ($_ -split '\W+').Count -eq 24 }
# output on screen
$result
# write output to new file
$result | Out-File -FilePath 'D:\test24.txt' -Force

Is there a way to display the lines of text that meets a condition with PowerShell

$data = Select-String -Path $selectedDirectory\$sqlFile -Pattern "GRANT" -Context 5,5
I want to use PowerShell to read .SQL files and we want to make sure that a user isn't using GRANT or DROP or DELETE without a human reviewing the file to see if it's okay.
My 1 line only is looking at GRANT but I don't think it's working.
If the keywords are in the file, I want to display a portion of the text on the screen +/- 5 lines of where the offending text was found.
Is there a way to change the color of the text for the specific line that has the offending search criteria (all other lines will be shown as default)
If you want colors displayed to the console, you will need to utilize Write-Host.
$data = Select-String -Path $selectedDirectory\$sqlFile -Pattern "GRANT|DROP|DELETE" -Context 5,5
$data | Foreach-Object {
$_.Context.Precontext
Write-Host $_.Line -ForeGroundColor Cyan
$_.Context.Postcontext
}
I'll give it a shot.
This function takes a file, searches for those keywords, and then prints +/- 5 lines. It's easy enough that I'm sure you know how it works and how to modify it. You can find the reference for the matchinfo class (returned by Select-String( here.
Function Get-SQLForbiddenWords ($sqlDataFile) {
$data = Select-String -Path $sqlDataFile -Pattern "GRANT|DROP|DELETE"
Foreach ( $line in $data) {
$lineNumberS = $line.LineNumber - 5
$lineNumberE = $line.LineNumber + 5
echo ('Bad Command Detected: {0}' -f $line.line)
(Get-Content $sqlDataFile)[$lineNumberS..$lineNumberE]
echo "`n"
}
}
It was pretty fun. Output:
Bad Command Detected: DROP
this is the sixth line
GRANT
this is the seventh line
this is the eighth line
DROP
this is the ninth line
this is the tenth linet
this is the eleventh line
this is the twelfbthfbth line
For starters, "GRANT" should be in quotes to denote a string.
If you notice, $line = Select-String -Pattern "Grant" will return an object.
If you look at the properties of the object using Get-Member, one of them is LineNumber
If you have read the contents of your file using $data = Get-Content File.sql or any something similar, you will have your data as an array object. Now you can now use this line number to extract the +/- 5 lines as you wish like $data[50..60]. This will show output lines from 50th to 60th line. You can easily replace the 50 and 60 with your variables.
Another way is to use the oss function (=Out-String -Stream).
Select-String "\b(GRANT|DROP|DELETE)\b" .\test.txt -Context 5 | oss | foreach { Write-Host $_ -ForegroundColor ("White","Cyan")[$_[0] -eq '>'] }
The following may make it a bit more readable.
Select-String "\b(GRANT|DROP|DELETE)\b" .\test.txt -Context 5 | Out-String -Stream | foreach {
if(-not $_) { return }
$fileName,$lineNumber,$line = $_.Split(":", 3)
$color = if($_.StartsWith(">")) { "Cyan" } else { "White" }
Write-Host $fileName $lineNumber.PadLeft(3, "0") $line -ForegroundColor $color -Separator " "
}

Get all lines containing a string in a huge text file - as fast as possible?

In Powershell, how to read and get as fast as possible the last line (or all the lines) which contains a specific string in a huge text file (about 200000 lines / 30 MBytes) ?
I'm using :
get-content myfile.txt | select-string -pattern "my_string" -encoding ASCII | select -last 1
But it's very very long (about 16-18 seconds).
I did tests without the last pipe "select -last 1", but it's the same time.
Is there a faster way to get the last occurence (or all occurences) of a specific string in huge file?
Perhaps it's the needed time ...
Or it there any possiblity to read the file faster from the end as I want the last occurence?
Thanks
Try this:
get-content myfile.txt -ReadCount 1000 |
foreach { $_ -match "my_string" }
That will read your file in chunks of 1000 records at a time, and find the matches in each chunk. This gives you better performance because you aren't wasting a lot of cpu time on memory management, since there's only 1000 lines at a time in the pipeline.
Have you tried:
gc myfile.txt | % { if($_ -match "my_string") {write-host $_}}
Or, you can create a "grep"-like function:
function grep($f,$s) {
gc $f | % {if($_ -match $s){write-host $_}}
}
Then you can just issue: grep $myfile.txt $my_string
$reader = New-Object System.IO.StreamReader("myfile.txt")
$lines = #()
if ($reader -ne $null) {
while (!$reader.EndOfStream) {
$line = $reader.ReadLine()
if ($line.Contains("my_string")) {
$lines += $line
}
}
}
$lines | Select-Object -Last 1
Have you tried using [System.IO.File]::ReadAllLines();? This method is more "raw" than the PowerShell-esque method, since we're plugging directly into the Microsoft .NET Framework types.
$Lines = [System.IO.File]::ReadAllLines();
[Regex]::Matches($Lines, 'my_string_pattern');
I wanted to extract the lines that contained failed and also write this lines to a new file, I will add the full command for this
get-content log.txt -ReadCount 1000 |
>> foreach { $_ -match "failed" } | Out-File C:\failes.txt