PowerShell - строка перезаписи, написанная с помощью Write-Host

Я пытаюсь перезаписать строку в PowerShell, написанную с помощью Write-Host (у меня есть процесс, выполняющийся в цикле, и я хочу показать процент, обновленный на экране). Я пытался это сделать:

Write-Host -NoNewline "`rWriting $outputFileName ($i/$fileCount)... $perc%"

но вместо того, чтобы переписать строку, он остается в одной строке и добавляет к ней.

что мне здесь не хватает?

Спасибо

Ответ 1

Вы не можете перезаписать строку в окне Powershell. Что вы можете сделать, это пустое окно с cls (Clear-Host):

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

Но вы действительно должны использовать Write-Progress, специально созданный для этой цели командлет:

# loop code
Write-Progress -Activity "Writing $outputFileName" -PercentComplete $perc
# end loop

Подробнее о Write-Progress здесь: http://technet.microsoft.com/en-us/library/hh849902.aspx

Ответ 2

Как настроить ответ 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

Ответ 3

Это не идеально, но вот script, который имеет вращающийся характер. Часть, которая позволяет вам сделать это:

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

Получить текущую позицию и сохранить ее, чтобы мы могли ссылаться на нее. По мере продвижения вы меняете $host.UI.RawUI.CursorPosition. Поскольку он был сохранен ранее, вы можете reset вернуть его $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 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 ""

Итак, как журнал, как вы помните, куда вы хотите вернуться, вы можете использовать эту логику. Это не будет работать должным образом в ISE. Вы также можете использовать символ `b как символ обратного пространства.

Ответ 4

Вы можете использовать класс консоли .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)

Ответ 5

Если целью является строгое переписывание строки подсказки командной строки powershell (текущая строка с помощью курсора), то все ответы здесь работают только до некоторой степени и в некотором смысле делают больше, чем это необходимо.

Ответы Raf и Craig, которые используют командлет Clear-Host (cls) в их первой строке, как отметил Даллсон, делают слишком много. При заполнении всего экрана предполагается, что очищенные вещи больше не важны для просмотра, что может быть неверным. Иногда это необходимо для понимания текущей строки.

Решение Raf Write-Progress - это мощный командлет, но кажется излишним для перезаписи текущей строки.

Предложение Raf Write-Host, подача Мэтта и настройка 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

Ответ 6

Я знаю, это довольно старый, но я был в той же ситуации и изменил решение от 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 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"
  }
}

Используйте функцию следующим образом:

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

В результате появится индикатор выполнения, который будет выглядеть так (в одной строке):

Запуск длинной задачи ██████████████████░░░░░░░░░░░░░░░░░ [4.242/ 8.192] (51,78%) Работа на позиции 4242

Надеюсь, это поможет кому-то еще

Ответ 7

Этот я получил из поста в блоге Томаса Рейнера. Он использует ANSI Escape-последовательности для сохранения позиции курсора [s и обновления позиции курсора [u

$E=[char]27

Затем сохраните текущую позицию курсора, используя escape-последовательность сохранения:

"${E}[s"

Использование: Используйте последовательность обновления ${E}[u чтобы указать PS, где начинать строку:

1..10 | %{"${E}[uThere are $_ s remaining"; Start-Sleep -Seconds 1}

Однако не работает в ISE.

Я знаю, что ссылки становятся устаревшими, но это здесь сегодня.

Ответ 8

https://241931348f64b1d1.wordpress.com/2017/08/23/how-to-write-on-the-same-line-with-write-output/

Этот метод работал для меня, чтобы записать выходное значение в цикл, пока его состояние не изменилось на "Успешно". Убедитесь, что вы установили курсор на нужное количество строк, и он перезаписывает ту же строку

while($val -ne 1)
    {
    if($taskstates.Tasks.state[0] -eq "Succeeded" -and $taskstates.Tasks.state[1] -eq "Succeeded" -and $taskstates.Tasks.state[2] -eq "Succeeded" -and $taskstates.Tasks.state[3] -eq "Succeeded")
        {
            $val = 1
        }
    #Clear-Host
    $taskstates.Tasks.StartTime[0].ToString() +" "+ $taskstates.Tasks.name[0] +" is "+ $taskstates.Tasks.state[0]
    $taskstates.Tasks.StartTime[1].ToString() +" "+ $taskstates.Tasks.name[1] +" is "+ $taskstates.Tasks.state[1]
    $taskstates.Tasks.StartTime[2].ToString() +" "+ $taskstates.Tasks.name[2] +" is "+ $taskstates.Tasks.state[2]
    $taskstates.Tasks.StartTime[3].ToString() +" "+ $taskstates.Tasks.name[3] +" is "+ $taskstates.Tasks.state[3]
    $taskstates = Get-ASRJob -Name $failoverjob.Name
    "ASR VMs build is in Progress"
    Start-Sleep 5
    [console]::setcursorposition($([console]::Cursorleft ),$([console]::CursorTop - 4))
    }

Ответ 9

Я опоздал на вечеринку. Вот подтверждение концепции, которую я недавно обнаружил и адаптировал для своих целей. Этот пример перезаписывает строку.

$count = 1

# Used for calculating the max number length for padding trailing spaces
$totalCount = 100

#Get current cursor position
$curCursorPos = New-Object System.Management.Automation.Host.Coordinates
$curCursorPos.X = $host.ui.rawui.CursorPosition.X
$curCursorPos.Y = $host.ui.rawui.CursorPosition.Y

# Counter code
While ($count -le 100) {


    # Keep cursor in the same position on the same line
    $host.ui.rawui.CursorPosition = $curCursorPos

    # Display with padded trailing spaces to overwrite any extra digits
    $pad = ($totalCount -as [string]).Length

    # Display the counter
    Write-Host "$(([string]$count).Padright($pad))" -NoNewline -ForegroundColor Green

    # Run through the example quickly
    Start-Sleep -Milliseconds 100

    #increment $count
    $count++

}

Вы можете поэкспериментировать со свойством Write-Host -NoNewline, сохранив или удалив его, чтобы увидеть, какой из них выглядит лучше для вас.

Ответ 10

Попробуйте

for ($i=1;$i -le 100;$i++){Write-Host -NoNewline "'r" $i;sleep 1}