2014-09-15 1 views
10

Sto provando a sovrascrivere una riga in PowerShell scritta con Write-Host (ho un processo in esecuzione in un ciclo e voglio mostrare la percentuale aggiornata sullo schermo). Quello che ho cercato di fare è questo:PowerShell - Linea di sovrascrittura scritta con Write-Host

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

ma invece di sovrascrivere la linea rimane sulla stessa linea e aggiunge ad esso.

cosa mi manca qui?

Grazie

+2

Solo un suggerimento, usando 'scrittura Progress' probabilmente fare qualcosa di simile a quello che si sta cercando di raggiungere. – arco444

+0

Strano. Questo ha funzionato bene per me ... –

+0

Avvolgi l'output in un '$ (...)' quindi è '' Write-Host -NoNewline $ ("' rWriting $ outputFileName ($ i/$ fileCount) ... $ perc% ")' ' – James

risposta

11

Non è possibile sovrascrivere una linea in una finestra PowerShell. Che cosa si può fare è vuota la finestra con cls (Clear-Host):

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

Ma che cosa si dovrebbe davvero utilizzare è Write-Progress, un cmdlet costruita appositamente per questo scopo:

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

Altro su Write-Progress qui: http://technet.microsoft.com/en-us/library/hh849902.aspx

+0

Non è esattamente quello che stavo cercando quando ho scritto questo post. Ma ho adottato la tua risposta, dal momento che (1) non sapevo che cmdlt - quindi ho imparato qualcosa di nuovo e mi è piaciuto. e (2) si adatta alle mie esigenze. non so se effettivamente quello che cercavo originariamente è possibile o no (secondo quello che ho letto dovrebbe essere) - ma per quanto mi riguarda questa è la mia risposta selezionata :) – developer82

+1

@Raf puoi aggiornato rispondi poiché la riga _Non puoi sovrascrivere una riga in una finestra di Powershell_ non è corretta. Mentre la risposta era meglio servita da un cmdlet integrato, altri potrebbero cercare qualcos'altro. – Matt

7

Non perfetto, ma qui c'è una sceneggiatura che ha un carattere rotante. La parte che ti permette di fare ciò è:

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

Prendi la posizione corrente e salvala in modo che possiamo continuare a fare riferimento ad essa. Man mano che avanzi cambi il $host.UI.RawUI.CursorPosition. Poiché è stato precedentemente salvato, è possibile ripristinarlo $host.UI.RawUI.CursorPosition = $origpos. Dovresti essere in grado di sperimentare.

$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 "" 

Così come registro, come si ricorda dove si vuole tornare a allora è possibile utilizzare questa logica. Questo non funzionerà correttamente in ISE. Puoi anche usare `b come carattere di back-back.

2

È possibile utilizzare la classe della console .NET per fare esattamente ciò che si desidera dove lo si desidera. Funziona solo in finestre della console e non in 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) 
6

Come un tweak per Raf's answer above, non c'è bisogno di pulire lo schermo ogni volta per aggiornare la vostra ultima linea. Chiamare Write-Host con -NoNewLine e il ritorno a capo `r è sufficiente.

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

Questo non funziona per me in PowerShell ISE – JustinJDavies

+4

Sì Il comportamento della console ISE è diverso da quello previsto, ma funziona sulla console di PowerShell. – DuLLSoN

4

Se l'obiettivo è strettamente sovrascrivere console PowerShell linea di prompt (la linea corrente con il cursore), quindi tutte le risposte qui funzionano solo in misura, e in qualche modo facendo più di quanto è desiderato.

Le risposte di Raf's e Craig che utilizzano il cmdlet Clear-Host (cls) nella prima riga, come notato da Dullson, stanno facendo troppo. Cancellare l'intero schermo presuppone che le cose cancellate non siano più importanti per la visualizzazione che potrebbero non essere vere. A volte questi sono necessari per dare un senso alla linea corrente.

La soluzione Raf's Write-Progress è un potente cmdlet ma sembra un eccesso per la sola sovrascrittura della riga corrente.

La proposta di Raf's Write-Host, la sottomissione di Matt e il tweak di Dullson sono buoni in cui è necessario aggiornare solo una posizione di un personaggio in una determinata posizione dello schermo o dove il testo della linea successiva è più lungo della corrente. In caso contrario, il testo della linea successiva sovrascriverebbe solo la linea corrente fino all'estensione della sua lunghezza, lasciando quelle parti della linea completata la cui posizione di lunghezza è più lunga della nuova per rimanere in vista insieme alla nuova linea.

Ad esempio, se il valore precedente era 10 e il nuovo valore è 9, ciò che verrà visualizzato è 90. Il 9 sovrascrive solo la porzione del valore precedente che è uguale alla sua lunghezza - 1. Quindi le soluzioni funzionano bene per incrementi ma non così bene per decrementi in cui la lunghezza del valore si riduce rispetto al precedente.

Il seguente blocco mostra come garantire la sovrascrittura totale (visiva) del testo della riga corrente con una nuova.

$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 
+0

Solo vedendo questo ora. La mia soluzione si basa sul salvataggio della posizione precedente. È possibile utilizzare la stessa logica per modificare più personaggi in posizioni diverse, purché ci si muova. l'uso del carattere backspace ti permetterà anche di rimuovere quanto vuoi. Non dipende da un singolo personaggio. Si è appena presentato in questo modo. – Matt

+0

Se non conosco la lunghezza della linea attualmente visualizzata e voglio sovrascriverla con spazi prima di scriverla, c'è un modo per scoprire la lunghezza di una linea attualmente visualizzata nella console? O leggere il carattere nella posizione corrente del cursore? –

2

lo so, questo è abbastanza vecchio, ma ero nella stessa situazione und modificato la soluzione dalla Boluwade Kujero, proprio perché scrivere righe vuote prima di scrivere il nuovo output può tradursi in una "tremolante" di uscita.

Quindi, nella seguente funzione, faccio semplicemente sovrascrivere la linea esistente, scrivere spazi vuoti fino a raggiungere la vecchia posizione del cursore e tornare all'ultimo carattere della nuova riga.

Inoltre ho aggiunto una barra di avanzamento ottico. Il progresso è calcolato dalla funzione attraverso determinati parametri:

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" 
    } 
} 

utilizzare la funzione in questo modo:

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

Il risultato sarà una progressbar esecuzione che sarà simile a questa (in una sola riga):

Esecuzione di un task lungo ██████████████████░░░░░░░░░░░░░░░░░ [4.242/ 8.192] (51,78%) Lavorando sulla posizione 424 2

Spero che questo vi aiuterà a qualcun altro

+0

Non è solo una versione complicata di 'Write-Progress'? – Matt