Why is the "Read-host" section running before the code? - powershell

I just wanted a .ps1 file that will run a simple line of powershell but not close instantly.
Ive tried to do "read-host -prompt " " " but it is displaying before the code is run and then still closes instantly
get-appxpackage -allusers | select name
read-host -prompt "Press enter to exit"
I expect the outcome to be I run the file and then get a chance to read the output within the powershell window before pressing something to exit. But the actual output is prompts to exit before the code is run and then it runs through the output and closes

After executing this line of code:
get-appxpackage -allusers | select name
You'll have some "pending" objects ready to return to the Powershell pipelines output stream. The objects can't be sent to the pipeline until Read-Host has finished (since Powershell will treat these objects as "output" of your ps1 file). After Read-Host has finished the objects are sent to the pipeline (via the output stream). Since there is no other cmdlet there (using the output of you ps1 file), Powershells default behavior is to output the pipeline content to the Powershell host.
As #Lee_Daily already mentioned in above comment, adding Out-Host will send the output of get-appxpackage -allusers | select name to the Powershell host. So get-appxpackage -allusers | select name | out-host no objects are queued in the output stream for further pipeline actions.
I would recommend you check following sources:
About pipeline
About redirection
Understanding streams
These are essential Powershell concepts you've to understand.
Hope that helps.

Tagging on to what the Lee and Moerwald have said.
Another way to stream real-time is using ForEach or ForEach-Object, it's also a bit more performant than the Out-Host approach, because of the not defaulting to writing to the screen. If that latter performance v screen write is important to you. If you don't want the screen output of Out-Host, send it to Null.
# Using Out-Host
Measure-Command {
get-appxpackage -allusers |
select name | Out-Host
}
Name
----
1527c705-...
...
InputApp
Microsoft.AAD.BrokerPlugin
Microsoft.AccountsControl
...
# Results
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
*** Milliseconds : 643 ***
Ticks : 6431627
TotalDays : 7.44401273148148E-06
TotalHours : 0.000178656305555556
TotalMinutes : 0.0107193783333333
TotalSeconds : 0.6431627
TotalMilliseconds : 643.1627
# User ForEach in a script Block
Measure-Command {
& { foreach ($item in get-appxpackage -allusers | select name)
{ "processing $item"}}
}
# Results
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
*** Milliseconds : 385 ***
Ticks : 3858318
TotalDays : 4.46564583333333E-06
TotalHours : 0.0001071755
TotalMinutes : 0.00643053
TotalSeconds : 0.3858318
TotalMilliseconds : 385.8318
# Standard ForEach-Object
Measure-Command {
get-appxpackage -allusers |
ForEach-Object {select name}
}
# Results
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
*** Milliseconds : 498 ***
Ticks : 4988494
TotalDays : 5.77371990740741E-06
TotalHours : 0.000138569277777778
TotalMinutes : 0.00831415666666667
TotalSeconds : 0.4988494
TotalMilliseconds : 498.8494

Related

Does the powershell cmdlet 'Stop-Service' prevent the rest of the script from executing until the service has stopped? [duplicate]

This question already has answers here:
In Windows Powershell, how can I wait for an event to be true before proceeding?
(2 answers)
Closed 3 months ago.
I am writing a script to shut down a list of services using the Stop-Service cmdlet.
My question is: does the Stop-Service cmdlet wait until the service has stopped before continuing with the rest of the script? Or does the script immediately continue after executing the Stop-Service command regardless of outcome?
Script below
[System.Collections.ArrayList]$Services = 'service1','service2','service3';
#Stop Services
foreach ($svcName in $Services) {
$serviceObj = Get-Service -name $svcName
if($serviceObj.Status -eq 'Running') {
Stop-Service $serviceObj
}
}
For example: if all services take 10 seconds to stop each, will this script take 10 seconds to run or 30? (Assuming successful shut down)
I am trying to determine how long this script will take on average.
As per my comment.
($Stopwatch = [Diagnostics.Stopwatch]::StartNew())
(
Get-Service |
Select-Object -Property Name, Status
) -match 'Running' |
Select-Object -First 3 |
Stop-Service
$Stopwatch.Elapsed
# Resuults
<#
Days : 0
Hours : 0
Minutes : 0
Seconds : 6
Milliseconds : 182
Ticks : 61827905
TotalDays : 7.15600752314815E-05
TotalHours : 0.00171744180555556
TotalMinutes : 0.103046508333333
TotalSeconds : 6.1827905
TotalMilliseconds : 6182.7905
#>

How do i extract values using specific keywords in powershell

I have a powershell script which outputs the below and i want to extract the value corresponding to Total Minutes in the output.
Thanks in advance
Hours : 20
Minutes : 15
Seconds : 21
Milliseconds : 616
Ticks : 11097216160848
TotalDays : 12.8440001861667
TotalHours : 308.256004468
TotalMinutes : 18495.36026808
TotalSeconds : 1109721.6160848
TotalMilliseconds : 1109721616.0848
Your output is likely an object in list form. This means the text left of the colons are the property names. You can reference them in numerous ways.
# piping to select-object
$output | Select-Object -Expand TotalMinutes
# using member access
$output.TotalMinutes

Get-NetFirewallRule is very slow

The Get-NetFirewallRule method checks the status for about half second. This is a very long time when I need to check more than a hundred names in the firewall.
# About a minute checked this
0..100 | % { Get-NetFirewallRule -DisplayName $_ }
Can I speed up this method? Thanks
It is faster to get all rules first. Then query the retrieved rules based on your criteria.
# Storing Rules in Variable First
$rules = Get-NetFirewallRule
$rules | Where DisplayName -in 0..100
# Using the pipeline only
Get-NetFirewallRule | Where DisplayName -in 0..100
Performance Test:
# One Rule At a Time
Measure-Command {0..10 |% { get-netfirewallrule -displayname $_ }}
Days : 0
Hours : 0
Minutes : 0
Seconds : 9
Milliseconds : 877
Ticks : 98770423
TotalDays : 0.000114317619212963
TotalHours : 0.00274362286111111
TotalMinutes : 0.164617371666667
TotalSeconds : 9.8770423
TotalMilliseconds : 9877.0423
# Get All Rules First
Measure-Command {$rules = Get-netfirewallrule; $rules | where displayname -in 0..10}
Days : 0
Hours : 0
Minutes : 0
Seconds : 1
Milliseconds : 44
Ticks : 10440671
TotalDays : 1.20841099537037E-05
TotalHours : 0.000290018638888889
TotalMinutes : 0.0174011183333333
TotalSeconds : 1.0440671
TotalMilliseconds : 1044.0671
Hmm, I guess it's not indexed by name or displayname? This is the wmi class it uses. I'm not sure it's worth delving into, but fyi.
Get-WmiObject MSFT_NetFirewallRule -Namespace Root\StandardCimv2 -filter 'creationclassname="MSFT|FW|FirewallRule|vm-monitoring-dcom"'
The firewall rules are stored in the registry, btw.
There's various get-netfirewall*filter commands that make accessing certain info faster, but not name or displayname.
You can try making your own hashtable of the displaynames.
Try putting this in a job, it will get way faster:
$job = Start-Job -ScriptBlock {
0..100 | % { Get-NetFirewallRule -DisplayName $_ }
}
$job | Wait-Job | Receive-Job

select-object affecting future out-default streams in a bad way

Powershell seems to tie select cmdlets to the default output stream, and not release it after a command finishes, affecting future command in unexpected ways. It is not consistent in how it does this, which makes generating predictable output impossible without explicitly directing output when writing the script.
This weirdness happens if you put the commands on separate lines, mix in different object generation cmdlets, etc. It doesn't happen if you run the commands on separate lines interactively, and it doesn't happen with objects automatially created on the command line, unless you mix in objects not automatically created on the command line.
I give command line interactive examples, but it happens if you put these commands into a script with each command on a spearate line and then run the script.
PS /home/dennis> get-date|select dayofweek ; get-date
DayOfWeek
---------
Monday
Monday
PS /home/dennis> "string1"|select length ; "string2"
Length
------
7
string2
And for fun, check out this one:
S /home/dennis> "string0" ;"string1"|select length ; get-host ;"string2" ;get-date; 567 ; get-host
string0
Length
------
7
1
string2
1
567
1
PS /home/dennis> cat test.ps1
"string0"
"string1"|select length
get-host
"string2"
get-date
567
(1..5)
get-host
PS /home/dennis> ./test.ps1
string0
Length
------
7
1
string2
1
567
1
2
3
4
5
1
...
This also affects objects which are not of the same type, and in fact, it affects objects which do not even have the properties in the select statement. Delaying is not an option, and explictly forcing the output with out-host or write-host will directly write to the powershell output device, making it useless to create a script that will be used to produce objects in a pipeline. It also messes up variables. Observe:
PS /home/dennis> $d = get-date | select dayofweek ; $e = get-date ; $d ; $e
DayOfWeek
---------
Monday
Monday
PS /home/dennis> $d
DayOfWeek
---------
Monday
PS /home/dennis> $e
Monday, August 5, 2019 12:33:47 PM
For those who are thinking, it is only a display issue, and the script can be written to display it correctly, I say again, this makes scripts useless as tools you can reuse in other scripts.
Observe how a pipeline inside a script affects commands in an independent interactive shell.
PS /home/dennis> cat test.ps1
"string0"
"string1"|select length
get-host
"string2"
get-date
567
get-host
PS /home/dennis> ./test.ps1|% {$_}
string0
Length
------
7
1
string2
1
567
1
PS /home/dennis> ./test.ps1|% {write-host $_}
string0
#{Length=7}
System.Management.Automation.Internal.Host.InternalHost
string2
8/5/19 12:50:54 PM
567
System.Management.Automation.Internal.Host.InternalHost
PS /home/dennis> ./test.ps1|% {$_|out-host}
string0
Length
------
7
Name : ConsoleHost
Version : 6.2.2
InstanceId : 4e46c643-1a9d-4c55-9151-b311f287a9cb
UI : System.Management.Automation.Internal.Host.InternalHostUserInterface
CurrentCulture : en-US
CurrentUICulture : en-US
PrivateData : Microsoft.PowerShell.ConsoleHost+ConsoleColorProxy
DebuggerEnabled : True
IsRunspacePushed : False
Runspace : System.Management.Automation.Runspaces.LocalRunspace
string2
Monday, August 5, 2019 1:20:24 PM
567
Name : ConsoleHost
Version : 6.2.2
InstanceId : 4e46c643-1a9d-4c55-9151-b311f287a9cb
UI : System.Management.Automation.Internal.Host.InternalHostUserInterface
CurrentCulture : en-US
CurrentUICulture : en-US
PrivateData : Microsoft.PowerShell.ConsoleHost+ConsoleColorProxy
DebuggerEnabled : True
IsRunspacePushed : False
Runspace : System.Management.Automation.Runspaces.LocalRunspace
In any shell script I expect that a command will execute independently of the previous command.
WhatTheFortran is the logic behind this behaviour? What is the official recommendation to avoid this unpredictability?
I know this is confusing. Any time select-object seems to output a table, format-table is actually running in the background. These format commands are kind of an illusion, and controlled by format files. But the object is still the same underneath.
$a = [pscustomobject]#{name='Joe'}
$b = [pscustomobject]#{address='here'}
$a,$b | format-table
name
----
Joe
$a,$b | format-list
name : Joe
address : here
Other workarounds:
$(get-date|select dayofweek ; get-date) | format-list
$("string1"|select length ; "string2") | format-list
$("string0" ;"string1"|select length ; get-host ;"string2" ;get-date; 567 ; get-host) | format-list
test.ps1 | format-list
The problem with the $d example, is the assignment to $d is only the first statement up to the first semicolon. This is a different issue.
$d = $(get-date | select dayofweek ; $e = get-date ; $d ; $e)
$d | format-list # 3 objects, unless you repeat the last line
An example like this works, because both commands output object types that have .format.ps1xml files. Once you use select-object, the output is a generic PSCustomObject. Actually, you can try putting get-date first in any script.
get-date;get-host
From Windows Powershell in Action:
Out-Default uses steppable pipelines to run the formatter
cmdlets [like format-table] to do its rendering and then
calls Out-Host to display the formatted output.

Trying to get the periodic restart recycle value of a specific App Pool

IIS Version: 8.5.9600.16384
PowerShell version: 4.0
I am trying to get the periodic restart recycle value of a specific apppool like below :
$b=(Get-ItemProperty 'IIS:\AppPools\testapppool' -Name `
Recycling.periodicRestart.schedule.collection).value
$b
It is giving me output like:
Days : 0
Hours : 5
Minutes : 36
Seconds : 0
Milliseconds : 0
Ticks : 201600000000
TotalDays : 0.233333333333333
TotalHours : 5.6
TotalMinutes : 336
TotalSeconds : 20160
TotalMilliseconds : 20160000
If I run:
Get-ItemProperty 'IIS:\AppPools\testapppool' -Name`
Recycling.periodicRestart.schedule.collection
I get output like:
value : 05:36:00
Attributes : {value}
ChildElements : {}
ElementTagName : add
Methods :
Schema : Microsoft.IIs.PowerShell.Framework.ConfigurationElementSchema
Again when I run below snippet, it give same value output :
[System.Reflection.Assembly]::LoadFrom( `
"C:\windows\system32\inetsrv\Microsoft.Web.Administration.dll" )
$serverManager = new-object Microsoft.Web.Administration.ServerManager
$b=$serverManager.ApplicationPools |where {$_.name -eq 'tetsapppool'}
$var=$b.Recycling.PeriodicRestart.schedule |select time
$var
Time
----
05:36:00
Please help me figuring out a way to assign only the value of the periodic restart recycle value to another variable so that, I can compare it and operate upon the value of the variable.