How do I output text without a newline in PowerShell? - powershell

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?

Write-Host -NoNewline "Enabling feature XYZ......."

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"

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

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)

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.

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.

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.

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.

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.'

A simplification to FrinkTheBrave's response:
[System.IO.File]::WriteAllText("c:\temp\myFile.txt", $myContent)

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.

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

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

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

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.

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

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}')

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

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

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

Related

Powershell 'mystring' vs Write-Host 'mystring' [duplicate]

I am having a little confusion about the various ways to print (echo) to the console. I have seen that there are multiple ways to write output to the console, such as:
Write-Host "Hello world1"
"Hello World2"
Out-Host -InputObject "Hello World3"
All three ways will print to the console. The middle one is somehow simpler and less verbose and easier to use. I also find that when you write a function such as:
function GetValues()
{
"1"
"2"
}
It still returns two strings in the pipeline:
And I'm still able to print out the values:
foreach ($s in GetValues)
{
Write-Host "s: " $s
}
The thing that I have found was that using just the quoted string doesn't always appear on custom hosts, and that I have had to use Write-Host to get values to print on custom hosts.
Somehow I find this confusing. Is "Print something" supposed to be an alias to Write-Host or what is the intent?
Default behaviour of PowerShell is just to dump everything that falls out of a pipeline without being picked up by another pipeline element or being assigned to a variable (or redirected) into Out-Host. What Out-Host does is obviously host-dependent.
Just letting things fall out of the pipeline is not a substitute for Write-Host which exists for the sole reason of outputting text in the host application.
If you want output, then use the Write-* cmdlets. If you want return values from a function, then just dump the objects there without any cmdlet.
The middle one writes to the pipeline. Write-Host and Out-Host writes to the console. 'echo' is an alias for Write-Output which writes to the pipeline as well. The best way to write to the console would be using the Write-Host cmdlet.
When an object is written to the pipeline it can be consumed by other commands in the chain. For example:
"hello world" | Do-Something
but this won't work since Write-Host writes to the console, not to the pipeline (Do-Something will not get the string):
Write-Host "hello world" | Do-Something

Powershell: Write-Output -NoEnumerate not suppressing output to console

I'm writing a function in PowerShell that I want to be called via other PowerShell functions as well as be used as a standalone function.
With that objective in mind, I want to send a message down the pipeline using Write-Output to these other functions.
However, I don't want Write-Output to write to the PowerShell console. The TechNet page for Write-Output states:
Write-Output:
Sends the specified objects to the next command in the pipeline. If the command is the last command in the pipeline, the objects are displayed in the console.
-NoEnumerate:
By default, the Write-Output cmdlet always enumerates its output. The NoEnumerate parameter suppresses the default behavior, and prevents Write-Output from enumerating output. The NoEnumerate parameter has no effect on collections that were created by wrapping commands in parentheses, because the parentheses force enumeration.
For some reason, this -NoEnumerate switch will not work for me in either the PowerShell ISE or the PowerShell CLI. I always get output to my screen.
$data = "Text to suppress"
Write-Output -InputObject $data -NoEnumerate
This will always return 'Text to suppress' (no quotes).
I've seen people suggest to pipe to Out-Null like this:
$data = "Text to suppress"
Write-Output -InputObject $data -NoEnumerate | Out-Null
$_
This suppresses screen output, but when I use $_ I have nothing in my pipeline afterwards which defeats the purpose of me using Write-Output in the first place.
System is Windows 2012 with PowerShell 4.0
Any help is appreciated.
Write-Output doesn't write to the console unless it's the last command in the pipeline. In your first example, Write-Output is the only command in the pipeline, so its output is being dumped to the console. To keep that from happening, you need to send the output somewhere. For example:
Write-Output 5
will send "5" to the console, because Write-Output is the last and only command in the pipeline. However:
Write-Output 5 | Start-Sleep
no longer does that because Start-Sleep is now the next command in the pipeline, and has therefore become the recipient of Write-Output's data.
Try this:
Write your function as you have written it with Write-Output as the last command in the pipeline. This should send the output up the line to the invoker of the function. It's here that the invoker can use the output, and at the same time suppress writing to the console.
MyFunction blah, blah, blah | % {do something with each object in the output}
I haven't tried this, so I don't know if it works. But it seems plausible.
My question is not the greatest.
First of all Write-Output -NoEnumerate doesn't suppress output on Write-Output.
Secondly, Write-Output is supposed to write its output. Trying to make it stop is a silly goal.
Thirdly, piping Write-Output to Out-Null or Out-File means that the value you gave Write-Output will not continue down the pipeline which was the only reason I was using it.
Fourth, $suppress = Write-Output "String to Suppress" also doesn't pass the value down the pipeline.
So I'm answering my question by realizing if it prints out to the screen that's really not a terrible thing and moving on. Thank you for your help and suggestions.
Explicitly storing the output in a variable would be more prudent than trying to use an implicit automatic variable. As soon as another command is run, that implicit variable will lose the prior output stored in it. No automatic variable exists to do what you're asking.
If you want to type out a set of commands without storing everything in temporary variables along the way, you can write a scriptblock at the command line as well, and make use of the $_ automatic variable you've indicated you're trying to use.
You just need to start a new line using shift + enter and write the code block as you would in a normal scriptblock - in which you could use the $_ automatic variable as part of a pipeline.

How to write to the console in PowerShell?

I am having a little confusion about the various ways to print (echo) to the console. I have seen that there are multiple ways to write output to the console, such as:
Write-Host "Hello world1"
"Hello World2"
Out-Host -InputObject "Hello World3"
All three ways will print to the console. The middle one is somehow simpler and less verbose and easier to use. I also find that when you write a function such as:
function GetValues()
{
"1"
"2"
}
It still returns two strings in the pipeline:
And I'm still able to print out the values:
foreach ($s in GetValues)
{
Write-Host "s: " $s
}
The thing that I have found was that using just the quoted string doesn't always appear on custom hosts, and that I have had to use Write-Host to get values to print on custom hosts.
Somehow I find this confusing. Is "Print something" supposed to be an alias to Write-Host or what is the intent?
Default behaviour of PowerShell is just to dump everything that falls out of a pipeline without being picked up by another pipeline element or being assigned to a variable (or redirected) into Out-Host. What Out-Host does is obviously host-dependent.
Just letting things fall out of the pipeline is not a substitute for Write-Host which exists for the sole reason of outputting text in the host application.
If you want output, then use the Write-* cmdlets. If you want return values from a function, then just dump the objects there without any cmdlet.
The middle one writes to the pipeline. Write-Host and Out-Host writes to the console. 'echo' is an alias for Write-Output which writes to the pipeline as well. The best way to write to the console would be using the Write-Host cmdlet.
When an object is written to the pipeline it can be consumed by other commands in the chain. For example:
"hello world" | Do-Something
but this won't work since Write-Host writes to the console, not to the pipeline (Do-Something will not get the string):
Write-Host "hello world" | Do-Something

What's the difference between "Write-Host", "Write-Output", or "[console]::WriteLine"?

There are a number of different ways to output messages. What is the effective difference between outputting something via Write-Host, Write-Output, or [console]::WriteLine?
I also notice that if I use:
write-host "count=" + $count
The + gets included in the output. Why's that? Shouldn't the expression be evaluated to produce a single concatenated string before it gets written out?
Write-Output should be used when you want to send data on in the pipe line, but not necessarily want to display it on screen. The pipeline will eventually write it to out-default if nothing else uses it first.
Write-Host should be used when you want to do the opposite.
[console]::WriteLine is essentially what Write-Host is doing behind the scenes.
Run this demonstration code and examine the result.
function Test-Output {
Write-Output "Hello World"
}
function Test-Output2 {
Write-Host "Hello World" -foreground Green
}
function Receive-Output {
process { Write-Host $_ -foreground Yellow }
}
#Output piped to another function, not displayed in first.
Test-Output | Receive-Output
#Output not piped to 2nd function, only displayed in first.
Test-Output2 | Receive-Output
#Pipeline sends to Out-Default at the end.
Test-Output
You'll need to enclose the concatenation operation in parentheses, so that PowerShell processes the concatenation before tokenizing the parameter list for Write-Host, or use string interpolation
write-host ("count=" + $count)
# or
write-host "count=$count"
BTW - Watch this video of Jeffrey Snover explaining how the pipeline works. Back when I started learning PowerShell I found this to be the most useful explanation of how the pipeline works.
Apart from what Andy mentioned, there is another difference which could be important - write-host directly writes to the host and return nothing, meaning that you can't redirect the output, e.g., to a file.
---- script a.ps1 ----
write-host "hello"
Now run in PowerShell:
PS> .\a.ps1 > someFile.txt
hello
PS> type someFile.txt
PS>
As seen, you can't redirect them into a file. This maybe surprising for someone who are not careful.
But if switched to use write-output instead, you'll get redirection working as expected.
Here's another way to accomplish the equivalent of Write-Output. Just put your string in quotes:
"count=$count"
You can make sure this works the same as Write-Output by running this experiment:
"blah blah" > out.txt
Write-Output "blah blah" > out.txt
Write-Host "blah blah" > out.txt
The first two will output "blah blah" to out.txt, but the third one won't.
"help Write-Output" gives a hint of this behavior:
This cmdlet is typically used in scripts to display strings and other
objects on the console. However, because the default behavior is to
display the objects at the end of a pipeline, it is generally not
necessary to use the cmdlet.
In this case, the string itself "count=$count" is the object at the end of a pipeline, and is displayed.
For usages of Write-Host, PSScriptAnalyzer produces the following diagnostic:
Avoid using Write-Host because it might not work in all hosts, does not work when there is no host, and (prior to PS 5.0) cannot be suppressed, captured, or redirected. Instead, use Write-Output, Write-Verbose, or Write-Information.
See the documentation behind that rule for more information. Excerpts for posterity:
The use of Write-Host is greatly discouraged unless in the use of commands with the Show verb. The Show verb explicitly means "show on the screen, with no other possibilities".
Commands with the Show verb do not have this check applied.
Jeffrey Snover has a blog post Write-Host Considered Harmful in which he claims Write-Host is almost always the wrong thing to do because it interferes with automation and provides more explanation behind the diagnostic, however the above is a good summary.
From my testing Write-Output and [Console]::WriteLine() perform much better than Write-Host.
Depending on how much text you need to write out this may be important.
Below if the result of 5 tests each for Write-Host, Write-Output and [Console]::WriteLine().
In my limited experience, I've found when working with any sort of real world data I need to abandon the cmdlets and go straight for the lower level commands to get any decent performance out of my scripts.
measure-command {$count = 0; while ($count -lt 1000) { Write-Host "hello"; $count++ }}
1312ms
1651ms
1909ms
1685ms
1788ms
measure-command { $count = 0; while ($count -lt 1000) { Write-Output "hello"; $count++ }}
97ms
105ms
94ms
105ms
98ms
measure-command { $count = 0; while ($count -lt 1000) { [console]::WriteLine("hello"); $count++ }}
158ms
105ms
124ms
99ms
95ms
Regarding [Console]::WriteLine() - you should use it if you are going to use pipelines in CMD (not in powershell). Say you want your ps1 to stream a lot of data to stdout, and some other utility to consume/transform it. If you use Write-Host in the script it will be much slower.

how to output a string to variable and console at the same time

is there an easy way in powershell to output a string to variable and console at the same time?
i want to capture the output of my script to a variable so i can analyze it in the end of the script, save it to a log file and also email to an operator.
my intent is to have a variable $output and add any output strings to it and also output to console immediately something like
$output="Process started"
$output=+"Doing step 1"
"Doing step 1"
$output=+"Doing step 2"
"Doing step 2"
so in the end I can save $output to a log file, email it and parse it.
I played with tee-object that might work for that purpose but unfortunately it would rewrite my $output variable instead of appending a string to it.
UPDATE
This is the final solution I decided to go with - thanks to manojlds!
$script:output = ""
filter mylog {
$script:output+= $_+"`n"
return $_
}
"doing step {0}" -f 1 | mylog
"doing step {0}" -f 2 | mylog
"doing step {0}" -f 3 | mylog
#in the end of the script
$script:output
There are so many ways to get your end goal:
In your script just have something like this:
"Process started"
<step1>
"Doing step 1"
<step2>
"Doing step 2"
...
Then run the script as
.\test.ps1 | Tee-Object -file log.txt
Note that the output is available to Tee-Object and hence the console as and when it occurs. You don't get the output only after the entire script runs. This is how pipeline works in Powershell. Objects are passed along downstream as and when they occur. Insert a sleep 10 in between as steps and see that the output comes as soon as it is available.
Also, you don't necessarily have to have another script ( the launcher.ps1 ) you are talking about. You can use functions, scriptblock etc. within your script.
Some other ways:
function test {
"Process started"
sleep 5
"Doing step 1"
sleep 5
"Doing step 2"
}
test | %{$output+=$_;$_}
#use output
write-host -fore blue $output
You can create a filter:
$script:output = ""
filter appendOutput {
$script:output+= $_
return $_
}
"Process started" | appendOutput
sleep 5
"Doing step 1" | appendOutput
sleep 5
"Doing step 2" | appendOutput
#use output
write-host -fore blue $script:output
There are probably many more ways of doing this.
Here's a nice trick, enclose your variable assignment in parenthesis. You can find more on this in the PowerShell language specification (section 7.1.1 Grouping parentheses) available to download here or view it online here:
PS > ($var=1)
1
PS >
I haven't messed with powershell enough to give a concrete answer, but if I were to do this in C I would exploit side effects.
//Psuedo-C
string con (oldString, newString) {
print(newString);
return oldString + newString;
}
Use function like so:
myString = con(myString, "Process started");
It would have the desired effect. (leaving aside correct C syntax and pedantry such as dealing with newlines) I don't know how to translate this to powershell.
What you want to do may be considered messy however. It might be clearer if you just explicitly output and log output and log one after the other in your code. Side effects inevitably come back to bite you. Keep things modular.
I was looking at something similar to this, with the exception I did not need to analyze it afterwards, just collect the output.
Something someone else might look at since it seems you have your answer is using PowerShell transcripts (Start-Transcript and Stop-Transcript).I found from this site that it does have some issues when you hit a server error, but he shows how he handled it.
To expand a bit on #manojlds answer, you can simply use the Tee-Object cmdlet, but rather than specifying a file for it to write to, you can tell it to write directly to a variable, like so:
.\test.ps1 | Tee-Object -Variable output
All of the output from the script/cmdlet/function/etc will be written to the console screen in real-time, as well as stored in the $output variable when the script finishes running. So you could then analyze it or write it to a file using that variable. This avoids you having to read the file contents into a variable after the operation completed.
Script will write data on output screen as well as store the same data in variable
get-process | Tee-Object -Variable test
$test can use further
("Process started" | out-host) | Set-Variable x ; $x