Comment générer du texte sans nouvelle ligne dans PowerShell?

146

Je veux que mon script PowerShell imprime quelque chose comme ceci:

Enabling feature XYZ......Done

Le script ressemble à ceci:

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

Mais Write-Outputimprime toujours une nouvelle ligne à la fin pour que ma sortie ne soit pas sur une seule ligne. Y a-t-il un moyen de faire cela?

Amit G
la source
3
Reproduction

Réponses:

164

Write-Host -NoNewline "Activation de la fonction XYZ ......."

Shay Levy
la source
63
Évalué parce que l'exemple de l'OP utilise spécifiquement Write-Output, qui a une fonction très différente de Write-Host. Les lecteurs doivent noter cette grande différence avant de copier / coller la réponse.
NathanAldenSr
5
Je suis d'accord avec @NathanAldenSr, Write-Host n'aide pas si vous essayez de sortir dans un fichier, etc.
stevethethread
6
Write-Hostn'est presque jamais la bonne réponse. C'est l'équivalent de faire >/dev/ttysous Unixland.
Mark Reed
2
Write-Progresspeut être approprié dans certains cas, voir l'exemple ci-dessous.
Thomas
12
Évalué parce que l'exemple du PO utilise spécifiquement Write-Output. Write-Outputn'a pas le -NoNewLineparamètre.
Slogmeister Extraordinaire
49

Malheureusement, comme indiqué dans plusieurs réponses et commentaires, Write-Hostpeut être dangereux et ne peut pas être redirigé vers d'autres processus et Write-Outputn'a pas le -NoNewlinedrapeau.

Mais ces méthodes sont les moyens "* nix" pour afficher la progression, la manière "PowerShell" de le faire semble être Write-Progress: il affiche une barre en haut de la fenêtre PowerShell avec des informations de progression, disponibles à partir de PowerShell 3.0, voir le manuel pour détails.

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

Et pour prendre l'exemple du PO:

# 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"
Thomas
la source
3
Je pense que c'est la meilleure solution pour montrer le statut. Si vous avez besoin d'un journal ou quelque chose, vous devez vivre avec le saut de ligne de Write-Output.
fadanner
1
D'accord, plus le point de l'affichage progressif est juste "d'être sophistiqué" pour l'installation en direct, il ne sert à rien de l'avoir dans les fichiers journaux: imprimer "commencer à faire quelque chose" puis "faire quelque chose"
Thomas
13

Bien que cela ne fonctionne pas dans votre cas (puisque vous fournissez une sortie informative à l'utilisateur), créez une chaîne que vous pouvez utiliser pour ajouter la sortie. Quand il est temps de le sortir, sortez simplement la chaîne.

Ignorant bien sûr que cet exemple est idiot dans votre cas mais utile dans son concept:

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

Affiche:

Enabling feature XYZ.......Done
shufler
la source
1
Cela peut fonctionner dans l'exemple spécifique fourni, mais il y a toujours un saut de ligne supplémentaire produit par Write-Output. Solution de contournement raisonnable, mais pas de solution.
Slogmeister Extraordinaire
Write-Output génère toujours une nouvelle ligne à la fin. Il n'y a aucun moyen de contourner cela avec cette cmdlet
shufler
7
Ce n'est pas le point puisque toute la sortie apparaît après l'installation de la fonction.
majkinetor
10
Je ne comprends pas, qui donne plus de 1 upwote à cette question, car ce n'est pas le point puisque toute la sortie apparaît APRÈS l'installation de la fonctionnalité
maxkoryukov
1
"En ignorant bien sûr que cet exemple est idiot dans votre cas mais utile dans son concept:"
shufler
6

Oui, comme les autres réponses ont des états, cela ne peut pas être fait avec Write-Output. Là où PowerShell échoue, tournez-vous vers .NET, il y a même quelques réponses .NET ici, mais elles sont plus complexes que nécessaire.

Utilisez simplement:

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

Ce n'est pas le PowerShell le plus pur, mais cela fonctionne.

Mark Travis
la source
5
Downvoté parce que cela se comporte exactement comme Write-Host, sauf que les gens ne s'y attendront pas.
JBert
4

Pour écrire dans un fichier, vous pouvez utiliser un tableau d'octets. L'exemple suivant crée un fichier ZIP vide, auquel vous pouvez ajouter des fichiers:

[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 utiliser:

[Byte[]] $text = [System.Text.Encoding]::UTF8.getBytes("Enabling feature XYZ.......")
[System.IO.File]::WriteAllBytes("C:\My.zip", $text)
FrinkTheBrave
la source
C'est un exemple génial!
Peter Mortensen le
2

Une simplification de la réponse de FrinkTheBrave:

[System.IO.File]::WriteAllText("c:\temp\myFile.txt", $myContent)
Russell Speight
la source
Cela ne répond pas du tout à la question.
NathanAldenSr
2
Mais c'est exactement ce que j'ai recherché et ce que j'attendais du titre de la question.
Patrick Roocks
2

Le problème que j'ai rencontré était que Write-Output fait en fait des sauts de ligne dans la sortie lors de l'utilisation de PowerShell v2, au moins vers stdout. J'essayais d'écrire un texte XML sur stdout sans succès, car il serait dur enveloppé au caractère 80.

La solution de contournement consistait à utiliser

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

Ce n'était pas un problème dans PowerShell v3. Write-Output semble y fonctionner correctement.

Selon la façon dont le script PowerShell est appelé, vous devrez peut-être utiliser

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

avant d'écrire dans stdout.

eythort
la source
2
Se comporte comme Write-Host, mais pire. Vous ne pouvez pas le diriger vers un fichier par exemple.
majkinetor
2

Il ne semble y avoir aucun moyen de le faire dans PowerShell. Toutes les réponses précédentes ne sont pas correctes, car elles ne se comportent pas comme elles le font Write-Outputmais plutôt comme celles Write-Hostqui n'ont pas ce problème de toute façon.

La solution ferme semble être utilisée Write-Hostavec le -NoNewLineparamètre. Vous ne pouvez pas diriger ceci, ce qui est généralement un problème, mais il existe un moyen de remplacer cette fonction comme décrit dans Write-Host => Exporter vers un fichier , afin que vous puissiez facilement lui faire accepter le paramètre pour un fichier de sortie. C'est encore loin d'être une bonne solution. Avec Start-Transcriptcela, c'est plus utilisable, mais cette cmdlet a des problèmes avec les applications natives.

Write-Outputne peut tout simplement pas faire ce dont vous avez besoin dans un contexte général.

majkinetor
la source
2

Write-Hostest terrible, un destructeur de mondes, mais vous pouvez l'utiliser simplement pour afficher la progression d'un utilisateur tout en utilisant Write-Outputpour se connecter (pas que l'OP ait demandé la journalisation).

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"
}
Eodeluga
la source
En fonction de vos besoins pour indiquer les progrès, il y a aussi Write-Progress.
chwarr
1

Vous ne pouvez tout simplement pas demander à PowerShell d'omettre ces nouvelles lignes embêtantes ... Il n'y a pas de script ou d'applet de commande qui le fasse. Bien sûr, Write-Host est un non-sens absolu, car vous ne pouvez pas en rediriger / rediriger!

Néanmoins, vous pouvez écrire votre propre fichier EXE pour le faire.C'est ce que j'ai expliqué comment faire dans la question Stack Overflow Comment sortir quelque chose dans PowerShell .

samthebest
la source
2
Information incorrecte. Comme Shay et Jay ont parfaitement répondu, ajoutez simplement -NoNewline comme premier argument.
David au HotspotOffice
2
C'est peut-être le cas maintenant @DavidatHotspotOffice, mais la dernière fois que j'ai touché une fenêtre Windows (il y a plus d'un an) qui ne fonctionnait pas, vous ne pouviez pas rediriger / rediriger depuis Write-Host. Pour être honnête, je n'ai pas eu la moindre patience pour POSH ou .NET, j'ai quitté après quelques mois et je suis retourné à Unix Land. drôle
samthebest
3
@DavidatHotspotOffice - En fait, il a raison. Il n'y a pas d'argument "NoNewLine" pour Write-Output, ce que la question d'origine posait. Il y a de bonnes raisons, semble-t-il, d'utiliser l'écriture-sortie - cette réponse a donc du sens. jsnover.com/blog/2013/12/07/write-host-consemed-harmful
James Ruskin
Évalué parce que la question demande une solution «dans PowerShell». L'écriture d'un EXE externe n'est pas «dans PowerShell».
Slogmeister Extraordinaire
1
@SlogmeisterExtraordinaire Ce n'est pas possible dans PowerShell donc ma réponse est raisonnable. Vous venez de voter parce que vous êtes tellement triste que vous devez travailler avec le pire système d'exploitation du monde qui a la pire coquille du monde.
samthebest
1

La réponse de shufler est correcte. En d'autres termes: au lieu de transmettre les valeurs à Write-Output en utilisant le ARRAY FORM,

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

ou l'équivalent par plusieurs appels à Write-Output,

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

concaténez d'abord vos composants dans une STRING VARIABLE:

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

Cela empêchera les CRLF intermédiaires provoqués par l'appel de plusieurs fois Write-Output (ou ARRAY FORM), mais bien sûr ne supprimera pas le CRLF final de la commandlet Write-Output. Pour cela, vous devrez écrire votre propre commandlet, utiliser l'une des autres solutions de contournement compliquées répertoriées ici ou attendre que Microsoft décide de prendre en charge l' -NoNewlineoption d'écriture-sortie.

Votre désir de fournir un indicateur de progression textuel à la console (c'est-à-dire "....") par opposition à l'écriture dans un fichier journal, doit également être satisfait en utilisant Write-Host. Vous pouvez accomplir les deux en collectant le texte du msg dans une variable pour l'écrire dans le journal ET en utilisant Write-Host pour fournir une progression à la console. Cette fonctionnalité peut être combinée dans votre propre commandlet pour une meilleure réutilisation du code.

David Rudd
la source
Je préfère de loin cette réponse aux autres. Si vous appelez des propriétés d'objets, vous ne pouvez pas les mettre entre guillemets, j'ai donc utilisé: Write-Output ($ msg = $ MyObject.property + "Some text I want to include" + $ Object.property)
Lewis
2
@Lewis Vous pouvez certainement inclure des propriétés d'objet dans une chaîne! Utilisez l'expression $ () pour entourer n'importe quelle variable, par exemple "$ ($ MyObject.Property) Some text I want to include $ ($ Object.property)"
shufler
Cela peut fonctionner dans l'exemple spécifique fourni, mais il y a toujours un saut de ligne supplémentaire produit par Write-Output, vous ne pouvez tout simplement pas le voir car c'est la dernière chose écrite. Solution de contournement raisonnable, mais pas de solution. Il peut y avoir quelque chose qui consomme la sortie résultante qui ne peut pas gérer la nouvelle ligne de fin.
Slogmeister Extraordinaire
1
Pas correcte. La solution ne peut se faire avec une seule commande.
majkinetor
Cela ne répond pas à la question. La sortie du fichier journal doit afficher l'opération sur le point d'être tentée afin que l'échec puisse être vu et retracé. La concaténation n'y parvient pas.
durette
0

Ce qui suit replacera le curseur au début de la ligne précédente. A vous de le placer dans la bonne position horizontale (en utilisant $ pos.X pour le déplacer latéralement):

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

Votre sortie actuelle est de 27 espaces, donc $ pos.X = 27 pourrait fonctionner.

Will Martin
la source
Cela n'a rien à voir avec la sortie de fichier.
Slogmeister Extraordinaire
Ce n'est pas si mal non plus. Il peut produire la sortie correcte si vous le faites $pos.Xégalement. Le problème est que si vous le dirigez vers un fichier, deux lignes distinctes apparaissent.
majkinetor
0

Ce n'est peut-être pas très élégant, mais il fait exactement ce qu'OP a demandé. Notez que l'ISE joue avec StdOut, donc il n'y aura aucune sortie. Afin de voir ce travail de script, il ne peut pas être exécuté dans l'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()
Slogmeister Extraordinaire
la source
1
Pas correcte. Si vous placez cela dans un fichier et dirigez sa sortie, rien n'apparaît.
majkinetor
Le piping vers un fichier n'était pas quelque chose que l'OP demandait. Et oui, [System.Console]ne peut pas être redirigé vers un fichier.
Slogmeister Extraordinaire
0

J'ai triché, mais je crois que c'est la seule réponse qui répond à toutes les exigences. À savoir, cela évite le CRLF de fin, fournit un endroit pour que l'autre opération se termine dans l'intervalle, et redirige correctement vers stdout si nécessaire.

$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.'
durette
la source
0
$host.UI.Write('Enabling feature XYZ.......')
Enable-SPFeature...
$host.UI.WriteLine('Done')
BG100
la source
0

o / p désiré: Activation de la fonction XYZ ...... Terminé

vous pouvez utiliser la commande ci-dessous

$ a = "Activation de la fonction XYZ"

Sortie d'écriture "$ a ...... Done"

vous devez ajouter une variable et une déclaration entre guillemets. j'espère que cela vous sera utile :)

Merci Techiegal

technicien
la source
L'écriture-sortie est préférable pour placer des objets dans le pipeline. Pour l'affichage de texte, Write-Host est fréquemment utilisé et plus récemment Write-Information est utilisé pour écrire dans le flux d'informations.
logicdiagram
0

Il y a déjà tellement de réponses ici que personne ne fera défiler ici. Quoi qu'il en soit, il existe également une solution pour écrire du texte sans nouvelle ligne à la fin, avec les éléments suivants:

Sortie de fichier avec encodage:

  # 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; }

Sortie de la console:

  # 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; }
SchLx
la source
-3

Vous pouvez absolument le faire. Write-Output a un indicateur appelé " NoEnumerate " qui est essentiellement la même chose.

Odorant
la source