211

I want my PowerShell script to print something like this:

Enabling feature XYZ......Done

The script looks something like this:

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

But Write-Output always prints a new-line at the end so my output isn't on one line. Is there a way to do this?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Amit G
  • 5,165
  • 4
  • 28
  • 29
  • 3
    Possible duplicate of [Avoiding newline in write-output](http://stackoverflow.com/questions/6150255/avoiding-newline-in-write-output) – Michael Freidgeim Dec 02 '16 at 02:39

21 Answers21

209

Write-Host -NoNewline "Enabling feature XYZ......."
Jay Bazuzi
  • 45,157
  • 15
  • 111
  • 168
Shay Levy
  • 121,444
  • 32
  • 184
  • 206
  • 110
    Downvoted because the OP's example specifically uses `Write-Output`, which has vastly different function than `Write-Host`. Readers should note this big discrepency before copy/pasting the answer. – NathanAldenSr Mar 07 '15 at 02:50
  • 7
    I agree with @NathanAldenSr, Write-Host does not help if you are trying to output to a file etc. – stevethethread Jun 03 '15 at 15:22
  • 10
    `Write-Host` is almost never the right answer. It's the equivalent of doing `>/dev/tty` in Unixland. – Mark Reed Sep 12 '15 at 13:46
  • 2
    `Write-Progress` may be appropriate in some cases, see example below. – Thomas BDX Sep 15 '15 at 14:09
  • Right, this does not use Write-Output. Write-Host has a mind (?) of its own. – Allen Jan 04 '22 at 13:55
  • 3
    `Write-Host` is the same as `Write-Information` and is redirectable as output stream 6. So you'd redirect it to a file as `6>output.txt`. It doesn't have a "mind of its own," it's just not knowledge you can bring over from Unixland, is all, so lots of people misunderstand it. – Naikrovek Jun 07 '22 at 14:32
  • I agree with @Naikrovek. @stevethethread Write-Host will still work if you were to output a file like this: `Write-Host -NoNewline "Enabling feature XYZ......." 6> output.txt `. People should read [this](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_output_streams?view=powershell-7.2). – digitalguy99 Jun 15 '22 at 23:49
  • 1
    Contrary to the criticisms of this answer, when calling PowerShell.exe from inside WSL2, `WriteHost` behaves perfectly - It writes to standard output so it can be redirected just fine. However, one caveat is that instead of simply not adding a trailing newline, `-NoNewline` deletes all newlines that are already in the text you're trying to output as well, which... at least personally, this is almost never what I want - what I want is to disable automatic append of a newline that wasn't there, not to strip out all newlines that I'm actually wanting to output. – mtraceur Mar 30 '23 at 20:39
64

Unfortunately, as noted in several answers and comments, Write-Host can be dangerous and cannot be piped to other processes and Write-Output does not have the -NoNewline flag.

But those methods are the "*nix" ways to display progression, the "PowerShell" way to do that seems to be Write-Progress: it displays a bar at the top of the PowerShell window with progress information, available from PowerShell 3.0 onward, see manual for details.

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

And to take the OP's example:

# 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"
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Thomas BDX
  • 2,632
  • 2
  • 27
  • 31
  • 3
    I think this is best solution for showing the status. If you need to have a log or something you have to life with the linebreak of Write-Output. – fadanner Jan 27 '17 at 08:53
  • 1
    Agreed, plus the point of progressive display is just "to be fancy" for live installing, there's no point in having it in log files: print "start doing something" then "done doing something" – Thomas BDX Jun 08 '17 at 20:40
12

While it may not work in your case (since you're providing informative output to the user), create a string that you can use to append output. When it's time to output it, just output the string.

Ignoring of course that this example is silly in your case but useful in concept:

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

Displays:

Enabling feature XYZ.......Done
shufler
  • 926
  • 7
  • 23
  • 2
    This may work in the specific example provided, but there is still an extra line feed produced by `Write-Output`. Reasonable workaround, but not a solution. – Slogmeister Extraordinaire Feb 26 '16 at 15:23
  • Write-Output always outputs a newline at the end. There is no way around that with this cmdlet – shufler Feb 26 '16 at 17:05
  • 13
    This is not the point since entire output appears after the feature is installed. – majkinetor Mar 25 '16 at 11:03
  • 17
    I don't understand, who gives more than 1 upwote to this question, because _This is not the point since **entire output appears AFTER the feature is installed**_ – maxkoryukov Apr 15 '18 at 18:39
  • 3
    "Ignoring of course that this example is silly in your case but useful in concept:" – shufler Jun 04 '18 at 21:36
  • @shufler It's not useful in concept at all. It's functionally no different from assigning the whole value to `$output` right off the bat. – JLRishe Nov 12 '20 at 18:22
  • @JLRishe yes, keeping in mind the question is has a constraint for using a cmdlet that displays a line of text with a linebreak and OP wants to display a single line of text from two lines of text coming from different places in the script. The idea is useful in concept because they may want to have exception handling around Enable-SPFeature that could result in different text being added to the string. Assigning "Enabling Feature XYZ ..." under several selection control statements is redundant. – shufler Nov 13 '20 at 19:19
5

There seems to be no way to do this in PowerShell. All of the previous answers are not correct, because they do not behave the way Write-Output behaves but more like Write-Host which doesn't have this problem anyway.

The closes solution seems to use Write-Host with the -NoNewLine parameter. You can not pipe this which is a problem generally, but there is a way to override this function as described in Write-Host => Export to a file, so you can easily make it accept the parameter for an output file. This is still far from a good solution. With Start-Transcript this is more usable, but that cmdlet has problems with native applications.

Write-Outputsimply can't do what you need in a general context.

EDIT 2023:

You can for craziness of it do something like this:

function log ($msg, [switch] $NoNewLine) { $script:_msg+=$msg; if ($NoNewLine) { Write-Host -NoNewline $msg } else { Write-Host $msg; $script:_msg >>output.txt; $script:_msg = '' } }

Test:

log 'enabling feature xyx ' -NoNewLine; 1..3 | % { sleep 1; log . -NoNewLine }; log ' ok'; cat output.txt

enabling feature xyx ... ok

majkinetor
  • 8,730
  • 9
  • 54
  • 72
5

Yes, as other answers have states, it cannot be done with Write-Output. Where PowerShell fails, turn to .NET, there are even a couple of .NET answers here but they are more complex than they need to be.

Just use:

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

It is not purest PowerShell, but it works.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mark Travis
  • 1,049
  • 9
  • 11
  • 13
    Downvoted because this behaves just like `Write-Host`, except people will not expect it. – JBert May 30 '17 at 10:07
  • `[Console]::Write` is even worse than `Write-Host`, which you can at least intercept if you know that it's fd 6. This bypasses all standard open fds and just hits the console directly. – Mark Reed Mar 21 '23 at 14:29
5

To write to a file you can use a byte array. The following example creates an empty ZIP file, which you can add files to:

[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)

Or use:

[Byte[]] $text = [System.Text.Encoding]::UTF8.getBytes("Enabling feature XYZ.......")
[System.IO.File]::WriteAllBytes("C:\My.zip", $text)
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
FrinkTheBrave
  • 3,894
  • 10
  • 46
  • 55
3

The problem that I hit was that Write-Output actually linebreaks the output when using using PowerShell v2, at least to stdout. I was trying to write an XML text to stdout without success, because it would be hard wrapped at character 80.

The workaround was to use

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

This was not an issue in PowerShell v3. Write-Output seems to be working properly there.

Depending on how the PowerShell script is invoked, you may need to use

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

before you write to stdout.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
eythort
  • 833
  • 8
  • 7
3

The answer by shufler is correct. Stated another way: Instead of passing the values to Write-Output using the ARRAY FORM,

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

or the equivalent by multiple calls to Write-Output,

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

concatenate your components into a STRING VARIABLE first:

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

This will prevent the intermediate CRLFs caused by calling Write-Output multiple times (or ARRAY FORM), but of course will not suppress the final CRLF of the Write-Output commandlet. For that, you will have to write your own commandlet, use one of the other convoluted workarounds listed here, or wait until Microsoft decides to support the -NoNewline option for Write-Output.

Your desire to provide a textual progress meter to the console (i.e. "....") as opposed to writing to a log file, should also be satisfied by using Write-Host. You can accomplish both by collecting the msg text into a variable for writing to the log AND using Write-Host to provide progress to the console. This functionality can be combined into your own commandlet for greatest code reuse.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
David Rudd
  • 47
  • 1
  • I much prefer this answer over the others. If you're calling properties of objects, you can't enclose them in quotes so I used: Write-Output ($msg = $MyObject.property + "Some text I want to include" + $Object.property) – Lewis Nov 02 '15 at 13:16
  • 2
    @Lewis You can certainly include object properties inside a string! Use the $() expression to surround any variable, e.g. "$($MyObject.Property) Some text I want to include $($Object.property)" – shufler Nov 07 '15 at 21:22
  • This may work in the specific example provided, but there is still an extra line feed produced by `Write-Output`, you just can't see it because it's the last thing written. Reasonable workaround, but not a solution. There may be something consuming the resultant output that can't handle the trailing newline. – Slogmeister Extraordinaire Feb 26 '16 at 15:27
  • 1
    Not correct. The solution can not be done with a single command. – majkinetor Mar 25 '16 at 11:10
  • This doesn't address the question. The log file output should show the operation that was about to be attempted so the failure can be seen and traced. Concatenation doesn't achieve that. – durette Apr 12 '19 at 20:09
3

I cheated, but I believe this is the only answer that addresses every requirement. Namely, this avoids the trailing CRLF, provides a place for the other operation to complete in the meantime, and properly redirects to stdout as necessary.

$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
  • 353
  • 1
  • 12
  • 1
    In `pwsh` (powershell core) [Add-Type](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/add-type?view=powershell-7.1) doesn't have the `-CompilerParameters` option, but you can do the same with other languages: `python3 -c "import sys; print(sys.argv[1], end='')" "Enabling feature XYZ......."` – Carl Walsh Oct 01 '21 at 18:13
2

You simply cannot get PowerShell to omit those pesky newlines... There is no script or cmdlet that does. Of course, Write-Host is absolute nonsense, because you can't redirect/pipe from it!

Nevertheless, you can write your own EXE file to do it which is what I explained how to do in Stack Overflow question How to output something in PowerShell.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
samthebest
  • 30,803
  • 25
  • 102
  • 142
  • 2
    Incorrect information. As Shay and Jay excellently answered, simply add -NoNewline as the first argument. – David at HotspotOffice Nov 18 '13 at 16:30
  • 2
    Maybe that's the case now @DavidatHotspotOffice but when I last touched a windows box (over a year ago) that didn't work, you couldn't redirect/pipe from Write-Host. To be fair I didn't have the slightest bit of patience for POSH or .NET, I quit after a few months and went back to unix land. [funny](http://thecodinglove.com/post/66696568857/when-i-try-to-use-powershell-after-not-using-it-for-a) – samthebest Nov 23 '13 at 10:01
  • 5
    @DavidatHotspotOffice - Actually, he's correct. There's no "NoNewLine" argument for Write-Output, which is what the original question was asking about. There are some good reasons, it seems, for using Write-Output - so this answer makes sense. http://www.jsnover.com/blog/2013/12/07/write-host-considered-harmful/ – James Ruskin Mar 07 '14 at 09:17
  • Downvoted because question is asking for a solution "in PowerShell". Writing an external EXE is not "In PowerShell". – Slogmeister Extraordinaire Feb 26 '16 at 15:14
  • 1
    @SlogmeisterExtraordinaire It's not possible in PowerShell therefore my answer is reasonable. Your just downvoting because your so sad that you have to work with the worlds worst operating system which has the worlds worst shell. – samthebest Feb 27 '16 at 15:20
  • @SlogmeisterExtraordinaire says "PowerShell is cool" then writes 7 lines of powershell that can be done in 1 in bash. Is that sarcasm? haha! – samthebest Mar 06 '16 at 12:18
2

A simplification to FrinkTheBrave's response:

[System.IO.File]::WriteAllText("c:\temp\myFile.txt", $myContent)
famousgarkin
  • 13,687
  • 5
  • 58
  • 74
Russell Speight
  • 344
  • 3
  • 8
2

Write-Host is terrible, a destroyer of worlds, yet you can use it just to display progress to a user whilst using Write-Output to log (not that the OP asked for logging).

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"
}
chwarr
  • 6,777
  • 1
  • 30
  • 57
eodeluga
  • 608
  • 2
  • 7
  • 20
  • 1
    Depending on your needs for indicating progress, there's also [`Write-Progress`](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/write-progress?view=powershell-7). – chwarr May 20 '20 at 22:36
1

The following will place the cursor back at beginning of the previous row. It's up to you to place it in the right horizontal position (using $pos.X to move it sideways):

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

Your current output is 27 spaces over, so $pos.X = 27 might work.

1
$host.UI.Write('Enabling feature XYZ.......')
Enable-SPFeature...
$host.UI.WriteLine('Done')
BG100
  • 4,481
  • 2
  • 37
  • 64
1

I'm not an expert by any means, but why not this:


Write-Output "hello" | ForEach-Object {  $PSItem.Trim() } | Do-Whatever

This maintains the pipeline semantics but just trims the new line characters before passing it on down the pipeline to whatever you need to do next. If that is writing to a file, so be it. If that is writing to the host, you can do that, but if you do write it to the host, remember to use | Write-Host -NoNewline

UPDATE: As per my comment below: "I see why my answer won't work.. Powershell inevitable appends a new line char as part of it's piping semantics when piping to external programs. See this: github.com/PowerShell/PowerShell/issues/5974 Therefore when I pass the trimmed text down the pipeline, the new line char will re-appear in the input."

Darrell
  • 1,905
  • 23
  • 31
  • Ok I see why my answer won't work.. Powershell inevitable appends a new line char as part of it's piping semantics when piping to external programs. See this: https://github.com/PowerShell/PowerShell/issues/5974 Therefore when I pass the trimmed text down the pipeline, the new line char will re-appear in the input. – Darrell Jan 20 '22 at 17:31
1

personally i just use something like that : write "the user $($user.firstname) is online"

if i don't use $() only $user is interpreted not $user.firstname

best regards

bfd
  • 11
  • 1
0

It may not be terribly elegant, but it does exactly what OP requested. Note that the ISE messes with StdOut, so there will be no output. In order to see this script work it can't be run within the 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()
0

Simplest way with in-line concatenation - and while moving across to 'Write-Output' instead; e.g. 2 tab characters (string) & then a literal/verbatim (string):

Write-Output ("`t`t" + '${devArg}')
Tyler2P
  • 2,324
  • 26
  • 22
  • 31
DennisVM-D2i
  • 416
  • 3
  • 8
-1

desired o/p: Enabling feature XYZ......Done

you can use below command

$a = "Enabling feature XYZ"

Write-output "$a......Done"

you have to add variable and statement inside quotes. hope this is helpful :)

Thanks Techiegal

  • Write-Output is preferred for putting objects out to the pipeline. For text display, Write-Host is frequently use and more recently Write-Information is used to write to the Information stream. – logicaldiagram Jun 04 '20 at 12:55
-1

From the link in the comments, that answer includes:

Write-Output "Some text $( $var.Name )"

which worked very well for me. The $( ) is not redundant if you need to ExpandProperty to get the individual value of Name, otherwise my output was this instead of the resolved value:

@{Name=Name; Address=Address; City=City}.Name
Zachary Scott
  • 20,968
  • 35
  • 123
  • 205
-3

You can absolutely do this. Write-Output has a flag called "NoEnumerate" that is essentially the same thing.