PowerShell - 覆盖用Write-Host编写的行

dev*_*r82 18 powershell

我正在尝试覆盖用Write-Host编写的PowerShell中的一行(我有一个循环运行的进程,我想在屏幕上显示更新百分比).我试图做的是:

Write-Host -NoNewline "`rWriting $outputFileName ($i/$fileCount)... $perc%"
Run Code Online (Sandbox Code Playgroud)

但它不是覆盖线,而是保持在同一条线上并附加到它上面.

我在这里想念的是什么?

谢谢

Raf*_*Raf 20

您无法在Powershell窗口中覆盖一行.你可以做的是用cls(Clear-Host)空白窗口:

# loop code
cls
Write-Host "`rWriting $outputFileName ($i/$fileCount)... $perc%"
# end loop
Run Code Online (Sandbox Code Playgroud)

但是你真正应该使用的是Write-Progress一个专门为此目的而构建的cmdlet:

# loop code
Write-Progress -Activity "Writing $outputFileName" -PercentComplete $perc
# end loop
Run Code Online (Sandbox Code Playgroud)

更多信息,请Write-Progress访问:http://technet.microsoft.com/en-us/library/hh849902.aspx

  • @Raf你可以更新你的回答,因为_你不能覆盖Powershell窗口中的一行_是不正确的.虽然答案最好由内置cmdlet提供,但其他人可能正在寻找其他内容. (2认同)
  • `Write-Host` 之所以能工作,是因为 `\`r` ,但为什么要使用 `cls` 呢?对于程序/脚本来说,清除 shell 以显示进度是非常不好的,因为它可以简单地将其附加到下一行。如果没有“cls”,就会发生这种情况。因此,如果您可以忍受屏幕上的侵入性,最好听第二部分并只使用“Write-Progress” (2认同)

Dul*_*son 14

作为对Raf上述答案的调整,您不必每次都擦除屏幕以更新最后一行.调用Write-Host-NoNewLine并回车`r就足够了.

for ($a=0; $a -le 100; $a++) {
  Write-Host -NoNewLine "`r$a% complete"
  Start-Sleep -Milliseconds 10
}
Write-Host #ends the line after loop
Run Code Online (Sandbox Code Playgroud)

  • 是的ISE控制台行为与预期不同,但它适用于powershell控制台. (6认同)
  • 这在PowerShell ISE中对我不起作用 (2认同)
  • 这个答案确实是所提出问题的正确答案。 (2认同)

Mat*_*att 10

它并不完美,但这里有一个旋转角色的脚本.允许您这样做的部分是:

$origpos = $host.UI.RawUI.CursorPosition
$origpos.Y += 1
Run Code Online (Sandbox Code Playgroud)

获取当前位置并保存,以便我们可以继续参考它.随着你的进步,你改变了$host.UI.RawUI.CursorPosition.由于之前保存过,您可以重置它$host.UI.RawUI.CursorPosition = $origpos.你应该能够尝试一下.

$scroll = "/-\|/-\|"
$idx = 0
$job = Invoke-Command -ComputerName $env:ComputerName -ScriptBlock { Start-Sleep -Seconds 10 } -AsJob

$origpos = $host.UI.RawUI.CursorPosition
$origpos.Y += 1

while (($job.State -eq "Running") -and ($job.State -ne "NotStarted"))
{
    $host.UI.RawUI.CursorPosition = $origpos
    Write-Host $scroll[$idx] -NoNewline
    $idx++
    if ($idx -ge $scroll.Length)
    {
        $idx = 0
    }
    Start-Sleep -Milliseconds 100
}
# It's over - clear the activity indicator.
$host.UI.RawUI.CursorPosition = $origpos
Write-Host 'Complete'

Remove-Variable('job')


$job = Start-Job -ScriptBlock { Start-Sleep -Seconds 10 }
while (($job.State -eq "Running") -and ($job.State -ne "NotStarted"))
{
    Write-Host '.' -NoNewline
    Start-Sleep -Seconds 1
}
Write-Host ""
Run Code Online (Sandbox Code Playgroud)

因此,记录您想要返回的日志,然后您可以使用此逻辑.这在ISE中无法正常工作.您也可以使用`b作为后退空格字符.


Cra*_*g P 6

您可以使用.NET控制台类在您想要的位置执行您想要的操作.仅适用于控制台窗口,而不适用于ISE.

cls
[Console]::SetCursorPosition(40,5)
[Console]::Write('Value of $i = ')
[Console]::SetCursorPosition(40,7)
[Console]::Write('Value of $j = ')
For ($i = 1; $i -lt 11; $i++)
{
    [Console]::SetCursorPosition(57,5)
    [Console]::Write($i)
    for ($j = 1; $j -lt 11; $j++)
    {
        [Console]::SetCursorPosition(57,7)
        [Console]::Write("$j ")  
        Start-Sleep -Milliseconds 200 
    }
    Start-Sleep -Milliseconds 200
}

[Console]::SetCursorPosition(40,5)
[Console]::Write("                    `n")
[Console]::SetCursorPosition(40,7)
[Console]::Write("                    `n")

[Console]::SetCursorPosition(0,0)
Run Code Online (Sandbox Code Playgroud)


Bol*_*ero 5

如果目标是严格覆盖Powershell控制台提示符行(带有光标的当前行),则此处的所有答案仅在一定程度上起作用,并且在某些方面做了超出期望的事情。

像Dullson指出的那样,在第一行中使用Clear-Host cmdlet(cls)的Raf和Craig的答案做得太多。消隐整个屏幕时,假定清除的内容对于查看不再重要,这可能并非如此。有时这些对于理解当前行是必要的。

Raf的Write-Progress解决方案是一个功能强大的cmdlet,但似乎仅覆盖当前行就显得过大了。

Raf的Write-Host建议,Matt的提交和Dullson的调整都很好,因为在确定的屏幕位置仅一个字符位置需要更新,或者后续行文本的长度比当前更长。如果不是这样,则后继行文本将仅覆盖当前行的长度,而使后继行中长度位置比新行长的部分与新行一起保留在视图中。

例如,如果先前的值为10,而新的值为9,则显示的值为90。数字9只会覆盖先前值的等于其长度-1的部分。因此,解决方案对增量有效,但不适用于对于值的长度与以前相比减少的递减效果非常好。

以下块显示了如何确保用新的行文本完全(可视)覆盖。

$LongString = "This string is long"
$ShortString = "This is short"

#Simulate typing a string on the console line
$L = 1
While ($L -le $LongString.Length)
{
    $Sub = $LongString.Substring(0,$L)
    Write-Host "`r$Sub" -NoNewline
    $L++
    # This sleep is just to simulate manual typing delay
    Start-Sleep -Milliseconds 20
}

# Now blank out the entire line with the space character " "
# The quantity of spaces should be equal to the length of the current text
# Which in this case is contained in $Sub.Length

$Blank = " "
For($L = 1; $L -le $Sub.Length; $L++)
    {            
        $Blank = $Blank + " "
    }
Write-Host "`r$Blank" -NoNewline

# Overwrite the blank console line with the new string    
$L = 1
While ($L -le $ShortString.Length)
{
    $Sub = $ShortString.Substring(0,$L)
    Write-Host "`r$Sub" -NoNewline
    $L++
    # This sleep is just to simulate delay in manual typing
    Start-Sleep -Milliseconds 20
}

# The following is not required if you want the Powershell prompt
# to resume to the next line and not overwrite current console line.
# It is only required if you want the Powershell prompt to return
# to the current console line.
# You therefore blank out the entire line with spaces again.
# Otherwise prompt text might be written into just the left part of the last
# console line text instead of over its entirety.

For($L = 1; $L -le $Sub.Length; $L++)
    {            
        $Blank = $Blank + " "
    }
Write-Host "`r$Blank" -NoNewline
Write-Host "`r" -NoNewline
Run Code Online (Sandbox Code Playgroud)


小智 5

我知道,那已经很老了,但我处于同样的情况并修改了 Boluwade Kujero 的解决方案,只是因为在写入新输出之前写入空行可能会导致“闪烁”输出。

所以在下面的函数中,我只是覆盖现有的行,写空格直到到达旧的光标位置,然后回到新行的最后一个字符。

此外,我添加了一个光学进度条。该函数通过给定的参数计算进度:

function Write-Status 
{
  param([int]$Current,
        [int]$Total,
        [string]$Statustext,
        [string]$CurStatusText,
        [int]$ProgressbarLength = 35)

  # Save current Cursorposition for later
  [int]$XOrg = $host.UI.RawUI.CursorPosition.X

  # Create Progressbar
  [string]$progressbar = ""
  for ($i = 0 ; $i -lt $([System.Math]::Round($(([System.Math]::Round(($($Current) / $Total) * 100, 2) * $ProgressbarLength) / 100), 0)); $i++) {
    $progressbar = $progressbar + $([char]9608)
  }
  for ($i = 0 ; $i -lt ($ProgressbarLength - $([System.Math]::Round($(([System.Math]::Round(($($Current) / $Total) * 100, 2) * $ProgressbarLength) / 100), 0))); $i++) {
    $progressbar = $progressbar + $([char]9617)
  }
  # Overwrite Current Line with the current Status
  Write-Host -NoNewline "`r$Statustext $progressbar [$($Current.ToString("#,###").PadLeft($Total.ToString("#,###").Length)) / $($Total.ToString("#,###"))] ($($( ($Current / $Total) * 100).ToString("##0.00").PadLeft(6)) %) $CurStatusText"

  # There might be old Text behing the current Currsor, so let's write some blanks to the Position of $XOrg
  [int]$XNow = $host.UI.RawUI.CursorPosition.X
  for ([int]$i = $XNow; $i -lt $XOrg; $i++) {
    Write-Host -NoNewline " "
  }
  # Just for optical reasons: Go back to the last Position of current Line
  for ([int]$i = $XNow; $i -lt $XOrg; $i++) {
    Write-Host -NoNewline "`b"
  }
}
Run Code Online (Sandbox Code Playgroud)

像这样使用函数:

For ([int]$i=0; $i -le 8192; $i++) {
    Write-Status -Current $i -Total 8192 -Statustext "Running a long Task" -CurStatusText "Working on Position $i"
}
Run Code Online (Sandbox Code Playgroud)

结果将是一个正在运行的进度条,如下所示(在一行中):

运行一个长任务??????????????????????????????????????? [4.242 / 8.192] ( 51,78 %) 在位置 4242 工作

希望这会帮助别人

  • 这不就是‘Write-Progress’的复杂版本吗? (5认同)