Overwrite PowerShell output strings onto the same line
Asked Answered
P

2

6

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!

Protuberate answered 17/9, 2018 at 15:11 Comment(0)
C
4

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.

Camelopardus answered 17/9, 2018 at 15:26 Comment(1)
You can add an -End { Write-Host } to the final ForEach-Object if you don't want to insert a final carriage return which potentially prevent the prompt from overwriting the progress output. I believe you can also use [Console]::Write() and [Console]::WriteLine() instead of Write-Host if you want.Dehnel
T
2

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 
}
Thulium answered 18/9, 2018 at 3:34 Comment(2)
can we pipe the output of write-host?Serried
@Deian, I'm not sure I understand the question. By default, Write-Host bypasses the pipeline (success output stream), so cannot be piped. However, you can pipe it if you apply redirection 6>&1Thulium

© 2022 - 2024 — McMap. All rights reserved.