Como faço para gerar texto sem uma nova linha no PowerShell?


145

Quero que meu script do PowerShell imprima algo como isto:

Enabling feature XYZ......Done

O script é mais ou menos assim:

Write-Output "Enabling feature XYZ......."
Enable-SPFeature...
Write-Output "Done"

Mas Write-Outputsempre imprime uma nova linha no final, para que minha saída não esteja em uma linha. Existe uma maneira de fazer isso?


Respostas:


165

Write-Host -NoNewline "Ativando o recurso XYZ ......."


62
Votado porque o exemplo do OP usa especificamente Write-Output, que tem uma função muito diferente da Write-Host. Os leitores devem observar essa grande discrepância antes de copiar / colar a resposta.
precisa saber é o seguinte

5
Concordo com @NathanAldenSr, Write-Host não ajuda se você está tentando saída para um arquivo etc.
stevethethread

6
Write-Hostquase nunca é a resposta certa. É o equivalente a fazer >/dev/ttyno Unixland.
Re

2
Write-Progresspode ser apropriado em alguns casos, veja o exemplo abaixo.
Thomas

12
Voto negativo porque o exemplo do OP usa especificamente Write-Output. Write-Outputnão possui o -NoNewLineparâmetro
Slogmeister Extraordinaire

49

Infelizmente, conforme observado em várias respostas e comentários, Write-Hostpode ser perigoso e não pode ser canalizado para outros processos e Write-Outputnão possui o -NoNewlinesinalizador.

Mas esses métodos são as maneiras "* nix" de exibir a progressão, a maneira como o "PowerShell" faz isso Write-Progress: exibe uma barra na parte superior da janela do PowerShell com informações sobre o progresso, disponível no PowerShell 3.0 em diante, consulte o manual para detalhes.

# Total time to sleep
$start_sleep = 120

# Time to sleep between each notification
$sleep_iteration = 30

Write-Output ( "Sleeping {0} seconds ... " -f ($start_sleep) )
for ($i=1 ; $i -le ([int]$start_sleep/$sleep_iteration) ; $i++) {
    Start-Sleep -Seconds $sleep_iteration
    Write-Progress -CurrentOperation ("Sleep {0}s" -f ($start_sleep)) ( " {0}s ..." -f ($i*$sleep_iteration) )
}
Write-Progress -CurrentOperation ("Sleep {0}s" -f ($start_sleep)) -Completed "Done waiting for X to finish"

E, tomando o exemplo do OP:

# For the file log
Write-Output "Enabling feature XYZ"

# For the operator
Write-Progress -CurrentOperation "EnablingFeatureXYZ" ( "Enabling feature XYZ ... " )

Enable-SPFeature...

# For the operator
Write-Progress -CurrentOperation "EnablingFeatureXYZ" ( "Enabling feature XYZ ... Done" )

# For the log file
Write-Output "Feature XYZ enabled"

3
Eu acho que essa é a melhor solução para mostrar o status. Se você precisa ter um log ou algo que tenha vida com a quebra de linha de Write-Output.
fadanner

1
Acordado, mais o ponto de exibição progressiva é apenas "para ser extravagante" para a instalação ao vivo, não há nenhum ponto em tê-lo em arquivos de log: print "começar a fazer algo", então "feito fazendo algo"
Thomas

13

Embora possa não funcionar no seu caso (como você está fornecendo uma saída informativa ao usuário), crie uma string que possa ser usada para anexar a saída. Quando chegar a hora de produzi-lo, basta imprimir a string.

Ignorando, é claro, que este exemplo é bobo no seu caso, mas útil no conceito:

$output = "Enabling feature XYZ......."
Enable-SPFeature...
$output += "Done"
Write-Output $output

Exibe:

Enabling feature XYZ.......Done

1
Isso pode funcionar no exemplo específico fornecido, mas ainda há um feed de linha extra produzido por Write-Output. Solução razoável, mas não uma solução.
Slogmeister Extraordinaire

A saída de gravação sempre gera uma nova linha no final. Não há como contornar isso com esse cmdlet
shufler

7
Este não é o ponto, pois a saída inteira aparece após a instalação do recurso.
majkinetor

10
Eu não entendo, que dá mais de 1 upwote a esta pergunta, porque este não é o ponto desde aparecerá no monitor inteiras depois que o recurso está instalado
maxkoryukov

1
"Ignorando, é claro, que este exemplo é bobo no seu caso, mas útil no conceito:"
shufler

6

Sim, como outras respostas têm estados, isso não pode ser feito com Write-Output. Onde o PowerShell falha, use o .NET, existem algumas respostas do .NET aqui, mas elas são mais complexas do que precisam.

Apenas use:

[Console]::Write("Enabling feature XYZ.......")
Enable-SPFeature...
Write-Output "Done"

Não é o PowerShell mais puro, mas funciona.


5
Votado porque isso se comporta exatamente como Write-Host, exceto que as pessoas não esperam isso.
JBert #

4

Para gravar em um arquivo, você pode usar uma matriz de bytes. O exemplo a seguir cria um arquivo ZIP vazio, no qual você pode adicionar arquivos:

[Byte[]] $zipHeader = 80, 75, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
[System.IO.File]::WriteAllBytes("C:\My.zip", $zipHeader)

Ou use:

[Byte[]] $text = [System.Text.Encoding]::UTF8.getBytes("Enabling feature XYZ.......")
[System.IO.File]::WriteAllBytes("C:\My.zip", $text)

Esse é um exemplo incrível!
Peter Mortensen


2

O problema que percebi foi que a Write-Output na verdade quebra a linha ao usar o PowerShell v2, pelo menos para o stdout. Eu estava tentando escrever um texto XML no stdout sem sucesso, porque seria difícil envolver o caractere 80.

A solução alternativa era usar

[Console]::Out.Write($myVeryLongXMLTextBlobLine)

Este não foi um problema no PowerShell v3. O Write-Output parece estar funcionando corretamente lá.

Dependendo de como o script do PowerShell é chamado, pode ser necessário usar

[Console]::BufferWidth =< length of string, e.g. 10000)

antes de escrever para stdout.


2
Comporta-se como Write-Host, mas o pior. Você não pode canalizá-lo para arquivar, por exemplo.
majkinetor

2

Parece não haver maneira de fazer isso no PowerShell. Todas as respostas anteriores não estão corretas, porque não se comportam da maneira como Write-Outputse comporta, mas são mais parecidas com as Write-Hostque não têm esse problema.

A solução de fechamento parece ser usada Write-Hostcom o -NoNewLineparâmetro Você não pode canalizar isso que geralmente é um problema, mas existe uma maneira de substituir essa função, conforme descrito em Host de Gravação => Exportar para um arquivo , para que você possa aceitar facilmente o parâmetro de um arquivo de saída. Isso ainda está longe de ser uma boa solução. Com Start-Transcriptisso, é mais utilizável, mas esse cmdlet tem problemas com aplicativos nativos.

Write-Outputsimplesmente não pode fazer o que você precisa em um contexto geral.


2

Write-Hosté terrível, um destruidor de mundos, mas você pode usá-lo apenas para exibir o progresso de um usuário enquanto estiver usando o Write-Outputlog (não que o OP solicite o log).

Write-Output "Enabling feature XYZ" | Out-File "log.txt" # Pipe to log file
Write-Host -NoNewLine "Enabling feature XYZ......."
$result = Enable-SPFeature
$result | Out-File "log.txt"
# You could try{}catch{} an exception on Enable-SPFeature depending on what it's doing
if ($result -ne $null) {
    Write-Host "complete"
} else {
    Write-Host "failed"
}

Dependendo das suas necessidades para indicar progresso, também há Write-Progress.
chwarr

1

Você simplesmente não pode fazer com que o PowerShell omita essas novas linhas desagradáveis ​​... Não existe script ou cmdlet. Obviamente, Write-Host é um absurdo absoluto, porque você não pode redirecionar / canalizar a partir dele!

No entanto, você pode escrever seu próprio arquivo EXE para isso, o que eu expliquei como fazer na questão do Stack Overflow. Como gerar algo no PowerShell .


2
Informação incorreta. Como Shay e Jay responderam excelentemente, basta adicionar -NoNewline como o primeiro argumento.
David em HotspotOffice

2
Talvez seja esse o caso agora no @DavidatHotspotOffice, mas quando toquei pela última vez em uma caixa do Windows (mais de um ano atrás) que não funcionava, não era possível redirecionar / canalizar do Write-Host. Para ser justo, eu não tinha a menor paciência para POSH ou .NET, parei depois de alguns meses e voltei à terra unix. engraçado
samthebest

3
@DavidatHotspotOffice - Na verdade, ele está correto. Não há argumento "NoNewLine" para Write-Output, que é o que a pergunta original estava perguntando. Parece que existem algumas boas razões para usar o Write-Output - portanto, essa resposta faz sentido. jsnover.com/blog/2013/12/07/write-host-considered-harmful
James Ruskin

Voto negativo porque a pergunta está pedindo uma solução "no PowerShell". Escrever um EXE externo não é "No PowerShell".
Slogmeister Extraordinaire

1
@SlogmeisterExtraordinaire Não é possível no PowerShell, portanto, minha resposta é razoável. Você acabou de votar porque está tão triste que precisa trabalhar com o pior sistema operacional do mundo, que tem o pior shell do mundo.
2741616

1

A resposta da shufler está correta. Em outras palavras: em vez de passar os valores para Write-Output usando o ARRAY FORM,

Write-Output "Parameters are:" $Year $Month $Day

ou o equivalente por várias chamadas para Write-Output,

Write-Output "Parameters are:"
Write-Output $Year
Write-Output $Month
Write-Output $Day
Write-Output "Done."

concatene seus componentes em uma STRING VARIABLE primeiro:

$msg="Parameters are: $Year $Month $Day"
Write-Output $msg

Isso impedirá os CRLFs intermediários causados ​​pela chamada Write-Output várias vezes (ou ARRAY FORM), mas é claro que não suprimirá o CRLF final do comando write-Output. Para isso, você precisará escrever seu próprio commandlet, usar uma das outras soluções complicadas listadas aqui ou aguardar até que a Microsoft decida dar suporte ao-NoNewline opção de Saída de gravação.

Seu desejo de fornecer um medidor de progresso de texto ao console (por exemplo, "...."), em vez de gravar em um arquivo de log, também deve ser satisfeito usando o Write-Host. Você pode realizar ambos coletando o texto da mensagem em uma variável para gravar no log E usando o Write-Host para fornecer progresso ao console. Essa funcionalidade pode ser combinada em seu próprio comando para obter a melhor reutilização de código.


Eu prefiro muito mais essa resposta que as outras. Se você está chamando propriedades de objetos, não pode incluí-las entre aspas, então usei: Write-Output ($ msg = $ MyObject.property + "Algum texto que eu quero incluir" + $ Object.property)
Lewis

2
@ Lewis Você certamente pode incluir propriedades de objetos dentro de uma string! Use a expressão $ () para envolver qualquer variável, por exemplo, "$ ($ MyObject.Property) Algum texto que eu queira incluir $ ($ Object.property)"
shufler

Isso pode funcionar no exemplo específico fornecido, mas ainda há um feed de linha extra produzido por Write-Output, você simplesmente não pode vê-lo porque é a última coisa escrita. Solução razoável, mas não uma solução. Pode haver algo consumindo a saída resultante que não pode lidar com a nova linha à direita.
Slogmeister Extraordinaire

1
Incorreto. A solução não pode ser feita com um único comando.
majkinetor

Isso não aborda a questão. A saída do arquivo de log deve mostrar a operação que estava prestes a ser tentada para que a falha possa ser vista e rastreada. A concatenação não alcança isso.
Durette

0

A seguir, colocará o cursor de volta no início da linha anterior. Cabe a você colocá-lo na posição horizontal correta (usando $ pos.X para movê-lo para o lado):

$pos = $host.ui.RawUI.get_cursorPosition()
$pos.Y -= 1
$host.UI.RawUI.set_cursorPosition($Pos)

Sua saída atual é de 27 espaços, portanto $ pos.X = 27 pode funcionar.


Isso não tem nada a ver com a saída do arquivo.
Slogmeister Extraordinaire

Também não é tão ruim assim. Pode produzir a saída correta, se você $pos.Xtambém. O problema é que, se você o canalizar para um arquivo, duas linhas separadas serão exibidas.
majkinetor

0

Pode não ser muito elegante, mas faz exatamente o que o OP solicitou. Note que o ISE mexe com StdOut, então não haverá saída. Para ver este script funcionar, não pode ser executado dentro do ISE.

$stdout=[System.Console]::OpenStandardOutput()
$strOutput="Enabling feature XYZ... "
$stdout.Write(([System.Text.Encoding]::ASCII.GetBytes($strOutput)),0,$strOutput.Length)
Enable-SPFeature...
$strOutput="Done"
$stdout.Write(([System.Text.Encoding]::ASCII.GetBytes($strOutput)),0,$strOutput.Length)
$stdout.Close()

1
Incorreto. Se você colocar isso em um arquivo e canalizar sua saída, nada será exibido.
majkinetor

A canalização para um arquivo não foi solicitada pelo OP. E sim, [System.Console]não pode ser redirecionado para um arquivo.
Slogmeister Extraordinaire

0

Eu trapacei, mas acredito que esta é a única resposta que atende a todos os requisitos. Nomeadamente, isso evita o CRLF à direita, fornece um local para a outra operação ser concluída nesse meio tempo e redireciona adequadamente para o stdout, conforme necessário.

$c_sharp_source = @"
using System;
namespace StackOverflow
{
   public class ConsoleOut
   {
      public static void Main(string[] args)
      {
         Console.Write(args[0]);
      }
   }
}
"@
$compiler_parameters = New-Object System.CodeDom.Compiler.CompilerParameters
$compiler_parameters.GenerateExecutable = $true
$compiler_parameters.OutputAssembly = "consoleout.exe"
Add-Type -TypeDefinition $c_sharp_source -Language CSharp -CompilerParameters $compiler_parameters

.\consoleout.exe "Enabling feature XYZ......."
Write-Output 'Done.'

0
$host.UI.Write('Enabling feature XYZ.......')
Enable-SPFeature...
$host.UI.WriteLine('Done')

0

o / p desejado: Ativando o recurso XYZ ...... Concluído

você pode usar o comando abaixo

$ a = "Ativando o recurso XYZ"

Saída de gravação "$ a ...... Concluído"

você precisa adicionar variável e instrução dentro de aspas. espero que isso seja útil :)

Obrigado Techiegal


A saída de gravação é preferida para colocar objetos no pipeline. Para exibição de texto, o Write-Host é usado com freqüência e, mais recentemente, o Write-Information é usado para gravar no fluxo de Informações.
logicdiagram 04/06

0

Já há muita resposta aqui, ninguém vai rolar aqui. De qualquer forma, também existe uma solução para escrever texto sem uma nova linha no final, com o seguinte:

Saída de arquivo com codificação:

  # a simple one liner
  "Hello World, in one line" | Out-File -Append -Encoding ascii -NoNewline -FilePath my_file.txt;

  # this is a test to show how it works
  "Hello World".Split(" ") | % { "_ $_ _" | Out-File -Append -Encoding ascii -NoNewline -FilePath my_file.txt; }

Saída de consol:

  # a simple one liner
  "Hello World, in one line" | Write-Host -NoNewline;

  # this is a test to show how it works
  "Hello World".Split(" ") | % { "_ $_ _" | Write-Host -NoNewline; }

-3

Você pode absolutamente fazer isso. Write-Output tem um sinalizador chamado " NoEnumerate " que é essencialmente a mesma coisa.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.