Contain Write-host in a variable - powershell

I am after storing write-host as a variable for multiple lines
So I want to encompass the write-host section below so it is essentially copied and pasted to the beginning of the loop, updating certain parameters.
the script is to check a file and see how many lines are in the file, this then carries out a function on each line, I want to update the user on the progress, however during the loop I cls and want to update the progress. Simple progress bar.
However I am unable to do so, so I created a variable that stores this information and updates accordingly.~
The varible only contains the information I want to update, see code.
I have tried the following;
Surrounding in parenthese,
using , to change to a new line,
Encasing the statement in ().
$56 = (Write-Host "This is a test" -foregroundcolor green),
(Write-host "Same Test only bigger"-foregroundcolor red) ,
"No!",
"I am King" $King ++
#$56
Output is
This is a test
Same Test only bigger
I want it to only display if I use the variable $56 as you can see it pulls the write-host without the variable.

Write-Host doesn't return its output as an object; it bypasses the pipeline to go directly to the console, effectively "returning" a null string. As such, you can't save the result of a Write-Host in a variable.
You can, however, save a script block in a variable, and execution of any statement in the script block variable will be deferred until the variable is invoked with the & expression, e.g.,
$fifty-six = { Write-Host "This is a test" -foregroundcolor green }
& $fifty-six
You should, however, consider carefully what your goal is, and whether there might not be a better way to do things. If you are interested in tracking the progress of a function, and you are using PowerShell 3.0 or later, you may want to look into Write-Progress instead.

I still think a function can do this:
function Output {
param([int]$lineNumber)
switch ($lineNumber) {
1 {Write-Host "This is a test" -foregroundcolor green}
2 {Write-host "Same Test only bigger"-foregroundcolor red}
3 {"No!"}
4 {"I am King"}
}
}
PS H:\> Output 1
This is a test
PS H:\> Output 4
I am King
I don't know what King is here, but if you want to use that as an increment, then you can just do this:
PS H:\> $King = 1
PS H:\> Output $King
This is a test
PS H:\> $King++
PS H:\> output $King
Same Test only bigger

Related

How to make write-host recognizable on TFS?

write-verbose prints out whatever output from powershell onto the TFS logging screen, but write-host doesnt.
what i like about write-host is the -foreground color option it has.
so i would like to retain the colors and hence i ask:
Ive tried write-host but nothing gets printed out on the TFS logging
how can i configure TFS logging to accept/recognize write-host just like it recognizes write-verbose?
There is a solution for the narrower requirements in the comments: Make TFS show Write-Host output without colors, continue having colors on local, and don't change all the Write-Host calls in code to something else.
The solution is to redefine Write-Host only for the TFS environment, such that calls to Write-Host end up using some supported Write- command (like Write-Verbose or Write-Output).
So you can add this to the start of your powershell script:
if($Env:BUILD_BUILDNUMBER) {
function Write-Host($object) {
Write-Output $object
}
}
Note there is an important limitation in this approach: If any function uses Write-Host and then returns some value, it's return value will include whatever was sent to Write-Host. For example, compare the value of $x before redefining Write-Host and after:
function test() {
Write-Host "hi"
return "bye"
}
$x = test
To avoid this, you can also use Write-Verbose or any other Write- instead of Write-Output, but they don't work well for non-primitive types. They will either error out or just show the type name. You can do $object.ToString() inside the Write-Host redefinition to prevent errors, but often that may just show the object's type name.
More info on TFS env variables: https://learn.microsoft.com/en-us/azure/devops/pipelines/scripts/powershell?view=azure-devops

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

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