UnluckyForSome9
UnluckyForSome9

Reputation: 311

Overwrite PowerShell output strings onto the same line

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!

Upvotes: 6

Views: 2741

Answers (2)

mklement0
mklement0

Reputation: 438273

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 
}

Upvotes: 2

marsze
marsze

Reputation: 17064

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.

Upvotes: 4

Related Questions