How i can get vars from a jobs? Something like that:
$Var=72
$Job=start-job -Name Test {$b=8;$a=777;while($true){$a+=1;sleep 1}}
$sum=(Receive-Job $Job).a + $Var
write-host $sum
Write-Host (Receive-Job $Job).b
This would output $i every second:
start-job { for ($i = 0; $i -lt 10; $($i++;sleep 1)) {$i} } | receive-job -wait -auto
0
1
2
3
4
5
6
7
8
9
Start-ThreadJob has an undocumented -StreamingHost parameter:
Start-ThreadJob { write-host hi } -StreamingHost $host
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
11 Job11 ThreadJob NotStarted False PowerShell write-host hi
PS C:\Users\js> hi
Related
So i have 2 variables
one include 4 services
service 1
service 2
service 3
service 4
second variable include all services which are running on computer
$servicesall = (Get-service | Group-Object -Property Name).Name
$servicestocheck = Get-Content
i want to log to file info like Service 1 is running, Service 2 is not running
I'm not sure if this is exactly what you're looking for but if you store the services you need in a text file you can do the following:
$services = Get-Content "C:\ServiceList.txt"
$statusReport = Get-Service -Name $services | Select-Object Name, Status
$statusReport | Out-File -path "c:\logfile.log"
Result:
Name Status
---- ------
spooler Running
winrm Stopped
wsearch Running
The name parameter in Get-Service -Name $services accepts an array of strings, which is why you don't need to do any sort of 'foreach' loop.
This may not be the best way to do this, but here you have something to work with:
$services = (
"vds",
"VSS",
"W32Time",
"abc",
"bcd"
)
foreach ($item in $services) {
$service = Get-Service -Name $item -ErrorAction SilentlyContinue
if ($service) {
$exist = $true
} else {
$exist = $false
$service = #{
status = "nope"
Name = $item
}
}
$service | select Status, Name, #{Name="Exist"; Expression={$exist}}
}
Result:
Status Name Exist
------ ---- -----
Stopped vds True
Stopped VSS True
Running W32Time True
nope abc False
nope bcd False
I have a script block/function that returns PSCustomObject followed by Write-Host.
I want to get the output first then print the write-host but I can't seem to figure it out.
function ReturnArrayList {
param (
[int] $number
)
[System.Collections.ArrayList]$folderList = #()
$folderObject = [PSCustomObject]#{
Name = 'John'
number = $number
}
#Add the object to the array
$folderList.Add($folderObject) | Out-Null
return $folderList
}
$sb = {
param (
[int] $number
)
[System.Collections.ArrayList]$folderList = #()
$folderObject = [PSCustomObject]#{
Name = 'John'
number = $number
}
#Add the object to the array
$folderList.Add($folderObject) | Out-Null
return $folderList
}
ReturnArrayList -number 5
#Invoke-Command -ScriptBlock $sb -ArgumentList 5
Write-Host "This write host should come later"
Result:
This write host should come after
Name number
---- ------
John 5
Desired result:
Name number
---- ------
John 5
This write host should come after
How can I get the return result first and print the write-host message?
Thank you for your help in advance!
You can force PowerShell to write the output from ReturnArrayList to the screen before reaching Write-Host by piping it to either one of the Format-* cmdlets or Out-Default:
$object = ReturnArrayList -number 5
$object |Out-Default
Write-Host "This write host should come later"
Result:
Name number
---- ------
John 5
This write host should come later
Beware that your ReturnArrayList function does not actually return an ArrayList - PowerShell will automatically enumerate the item(s) in $folderlist, and since it only contains one item, the result is just a single PSCustomObject, "unwrapped" from the ArrayList so to speak:
PS ~> $object = ReturnArrayList -number 5
PS ~> $object.GetType().Name
PSCustomObject
To preserve enumerable types as output from functions, you'll need to either use Write-Output -NoEnumerate, or wrap the it in an array using the , operator:
function ReturnArrayList {
param (
[int] $number
)
[System.Collections.ArrayList]$folderList = #()
$folderObject = [PSCustomObject]#{
Name = 'John'
number = $number
}
#Add the object to the array
$folderList.Add($folderObject) | Out-Null
return ,$folderList
# or
Write-Output $folderList -NoEnumerate
}
Data is usually output to the pipeline, while Write-Host bypasses the pipeline and writes to the console directly.
Using Write-Output instead of Write-Host will fix this issue. You can easily find more in-depth information on this topic, and when not to Write-Host.
I've created the following function for use cleaning up all references to com objects at the end of a script:
function TrashCompactor ($reflist) {
foreach ($ref in $Reflist){
[System.Runtime.InteropServices.Marshal]::ReleaseComObject([System.__ComObject]$ref) | out-null
[Runtime.InteropServices.Marshal]::FinalReleaseComObject($ref) | out-null
Remove-Variable $ref | out-null
}
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
Will Remove-variable work as I expected? Is there any harm to including [System.GC]::Collect()?
Yes, and no... as this...
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
... is a common and best practice.
Windows will always do a cleanup, but it's always clean up your environment when you are done.
As documented...
Clean Up Your PowerShell Environment by Tracking Variable Use
https://devblogs.microsoft.com/scripting/clean-up-your-powershell-environment-by-tracking-variable-use
And covered by this SO Q&A and accepted answer...
PowerShell release COM object
function Release-Ref ($ref) {
[System.Runtime.InteropServices.Marshal]::ReleaseComObject([System.__ComObject]$ref) | out-null
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
because I've noted that my comobject always stay alive, I think Powershell 2.0 is not able to remove comobject no more used.
[System.Runtime.InteropServices.Marshal]::ReleaseComObject( $ref )
and that SO is exactly what you are asking, so this question is really a duplicate.
My example, I use a prefix to my variable so they are easy to find and simple globally clean up.
# Assign results to a variable and output to the screen using variable squeezing
($ponMyShell = New-Object -com "Wscript.Shell")
($ponDate = Get-Date)
($ponProcess = Get-Process |
Select -First 3)
<#
# Results
Monday, 2 March, 2020 19:40:47
Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
186 14 2648 6800 0.14 15336 0 aesm_service
465 27 24300 34064 0.33 27612 22 ApplicationFrameHost
158 8 1928 4848 0.02 14268 0 AppVShNotify
SpecialFolders CurrentDirectory
-------------- ----------------
System.__ComObject C:\Windows\system32
#>
Get-Variable -Name 'pon*'
<#
# Results
Name Value
---- -----
ponDate 02-Mar-20 19:46:59
ponMyShell System.__ComObject
ponProcess {System.Diagnostics.Process (aesm_service), System.Diagnostics.Process (ApplicationFrameHost), System.Diagnostics.Process (AppVShNotify)}
#>
# Clear resource environment
Get-PSSession |
Remove-PSSession
<#
# Results
#>
[System.Runtime.InteropServices.Marshal]::
ReleaseComObject([System.__ComObject]$ponMyShell) |
Out-Null
<#
# Results
#>
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
<#
# Results
#>
Get-Variable -Name 'pon*' |
ForEach { Get-Variable -Name $_ |
Remove-Variable -Force }
# Validate clean-up
Get-Variable -Name 'pon*'
<#
# Results
#>
I'm trying to create a union of multiple tables in Powershell to output in a user-friendly format as a report, similar to a UNION query in SQL.
I have the following code:
$ft = #{auto=$true; Property=#("MachineName", "Status", "Name", "DisplayName")}
$hosts = #("svr001", "svr002")
$report = #()
ForEach ($h in $hosts) {
$results = Get-Service -CN $h -Name MyService
$report += $results | Format-Table #ft
# Other things occur here, which is why I'm creating $report for output later.
}
Write-Output $report
The output of this code is as follows:
MachineName Status Name DisplayName
----------- ------ ---- -----------
svr001 Running MyService MyServiceDisplayName
MachineName Status Name DisplayName
----------- ------ ---- -----------
svr002 Running MyService MyServiceDisplayName
Since you simply add arrays to do a union in Powershell (i.e.,
$union = #(0, 1, 2) + #(3, 4, 5)), my initial thought was that I should
get the following output:
MachineName Status Name DisplayName
----------- ------ ---- -----------
svr001 Running MyService MyServiceDisplayName
svr002 Running MyService MyServiceDisplayName
In retrospect, I think I understand why I do not get this output, but I'm
unclear how to create a union of the two tables from the first output example into a single table as in the second, and I haven't been able to locate anything in the docs or examples online that would send me in the right direction.
Move the Format-Table to the last command. Format-* cmdlets create special format-objects that you can't work with manually so theres no point in saving them. When you save the result of Format-* to an array, you're saving the "report" which is why you get two tables in the output (array consists of two reports).
Collect the data first, then use Format-Table when you want to display the results.
$ft = #{auto=$true; Property=#("MachineName", "Status", "Name", "DisplayName")}
$hosts = #("svr001", "svr002")
$report = #()
ForEach ($h in $hosts) {
$results = Get-Service -ComputerName $h -Name MyService
$report += $results
# Other things occur here, which is why I'm creating $report for output later.
}
#Write-Output is not necessary as it is default behaviour
$report | Format-Table #ft
Sample output (used wuauserv as servicename):
MachineName Status Name DisplayName
----------- ------ ---- -----------
localhost Stopped wuauserv Windows Update
frode-pc Stopped wuauserv Windows Update
The Get-Service Cmdlet also supports an array of strings for the -ComputerName parameter. This works for me:
Get-Service -CN $hosts -Name MyService | Format-Table #ft
Sample Output using wuauserv:
MachineName Status Name DisplayName
----------- ------ ---- -----------
Tim-SRV1 Running wuauserv Windows Update
Tim-SRV2 Stopped wuauserv Windows Update
I'm new to Powershell and I'm having an issue with the Get-Job command. In my script, I'm testing out multi-threading and am doing something like:
$Program = {
"Thread " + $args[0];
Start-Sleep 5;
}
Start-Job $Program -ArgumentList #($i) | Out-Null
The Start-Job call is actually in a loop in which I'm creating multiple jobs. Below this, I have:
Get-Job
"Jobs Running: " + $(Get-Job -State Running).count
If there are multiple jobs running, I will get output like:
Id Name State HasMoreData Location Command
-- ---- ----- ----------- -------- -------
2201 Job2201 Running True localhost ...
2199 Job2199 Running True localhost ...
2197 Job2197 Running True localhost ...
2195 Job2195 Running True localhost ...
2193 Job2193 Completed True localhost ...
2191 Job2191 Completed True localhost ...
2189 Job2189 Completed True localhost ...
2187 Job2187 Completed True localhost ...
Jobs Running: 4
But if there is only one job running, it seems that $(Get-Job -State Running).count isn't returning anything:
Id Name State HasMoreData Location Command
-- ---- ----- ----------- -------- -------
2207 Job2207 Running True localhost ...
Jobs Running:
As you can see, there is one job running, but $(Get-Job -State Running).count doesn't return anything. Any idea what's happening here? To me, it looks like that if there are multiple jobs, $(Get-Job -State Running) returns a collection of jobs which has the .count property, whereas if there is only one job, it returns just that job, and doesn't have the .count property. If this is the case (or if I'm doing something wrong), what command should I be using to get my expected result of $(Get-Job -State Running).count == 1?
Try using Measure-Object
$(Get-Job -State Running | Measure-Object).count
In PS 2.0 count only works on arrays. When Get-Job only returns one job, it returns it as an OBJECT, not an array. To make it work, you could e.g. force Get-Job to always return an array by using #(code) . Try this:
$Program = {
"Thread " + $args[0];
Start-Sleep 5;
}
Start-Job $Program -ArgumentList #($i) | Out-Null
Get-Job
"Jobs Running: " + $(#(Get-Job -State Running).count)