param(
$path = "C:\test"
)cls
(Measure-Command{
#
$s = [Text.StringBuilder]::new()
for($i=1; $i -le 1000; $i++){
$s.AppendLine("[$i] Str")
[IO.File]::WriteAllText("$path\file_$i.txt", $s)
}
#
}).TotalMilliseconds
Powershell.exe - 150
Powershell_ISE.exe - 2600
Pwsh.exe(7.1) - 3200
Everybody have the same results, or there is something wrong with my ISE and pscore?
Related
I have:
for($i = 1 ; $i -le 3; $i++)
{
Start-Process powershell.exe
}
but I don't know how I would make the new windows run a ping command. Could be done with an extra script but have no idea. Thanks
Start-Process has an -ArgumentList parameter that can be used to pass arguments to the new PowerShell process.
powershell.exe and pwsh.exe have a -Command parameter that can be used to pass them a command.
You can combine the two like this:
for ($i = 1; $i -le 3; $i++)
{
Start-Process powershell.exe -ArgumentList '-NoExit',"-Command ping 127.0.0.$i"
}
If you don't use the -NoExit parameter the window will close as soon as the ping command finishes.
As mentioned in the comments to the question it is also possible to ping multiple hosts using the Test-Connection command like this:
Test-Connection -TargetName 127.0.0.1,127.0.0.2
This has a downside though in that it seems to ping one after the other rather than doing it in parallel.
Another way to do much the same thing in parallel, and probably more PowerShell style is to use jobs:
$jobs = #()
for ($i = 1; $i -le 3; $i++)
{
$jobs += Start-ThreadJob -ArgumentList $i { PARAM ($i)
Test-Connection "127.0.0.$i"
}
}
Wait-Job $jobs
Receive-Job $jobs -Wait -AutoRemoveJob
Note: Start-ThreadJob is newish. If you're still stuck on version 5 of PowerShell that comes with Windows use Start-Job instead, it spawns new processes where as Start-ThreadJob doesn't.
Nitpickers' corner
For those in the comments saying that appending to an array is slow. Strictly a more PowerShell way of doing this is given below. For three items, however, you won't be able to measure the difference and the readability of the code is way lower. It's also rather diverging from the original question.
1..3 | % { Start-ThreadJob -ArgumentList $_ { PARAM($i) Test-Connection "127.0.0.$i" } } | Wait-Job | Receive-Job -Wait -AutoRemoveJob
Here's a pinger script I have to watch multiple computers.
# pinger.ps1
# example: pinger comp001
# pinger $list
param ($hostnames)
#$pingcmd = 'test-netconnection -port 515'
$pingcmd = 'test-connection'
$sleeptime = 1
$sawup = #{}
$sawdown = #{}
foreach ($hostname in $hostnames) {
$sawup[$hostname] = $false
$sawdown[$hostname] = $false
}
#$sawup = 0
#$sawdown = 0
while ($true) {
# if (invoke-expression "$pingcmd $($hostname)") {
foreach ($hostname in $hostnames) {
if (& $pingcmd -count 1 $hostname -ea 0) {
if (! $sawup[$hostname]) {
echo "$([console]::beep(500,300))$hostname is up $(get-date)"
# [pscustomobject]#{Hostname = $hostname; Status = 'up'; Date = get-date} # format-table waits for 2 objects
$sawup[$hostname] = $true
$sawdown[$hostname] = $false
}
} else {
if (! $sawdown[$hostname]) {
echo "$([console]::beep(500,300))$hostname is down $(get-date)"
# [pscustomobject]#{Hostname = $hostname; Status = 'down'; Date = get-date}
$sawdown[$hostname] = $true
$sawup[$hostname] = $false
}
}
}
sleep $sleeptime
}
Example usage (it beeps):
.\pinger comp001,comp002
comp001 is up 07/13/2022 12:07:59
comp002 is up 07/13/2022 12:08:00
I'm trying to run PowerShell jobs in parallel (500) but for some reason it seems most jobs run one after the other, any idea what I'm doing wrong?
$jobs = {
Param(
[string]$thread_number_VDM,
[string]$thread_number_FS
)
for ($i=1; $i -le 60; $i++) {
#blablabla check something for 60 seconds
# sleep 1
}
}
###### main #####
for ($x=1; $x -le 50; $x++) {
for ($z=1; $z -le 10; $z++) {
Start-Job -ScriptBlock $jobs -ArgumentList ($x, $z)
}
}
I've been wondering about the performance impact of functions in PowerShell.
Let's say we want to generate 100.000 random numbers using System.Random.
$ranGen = New-Object System.Random
Executing
for ($i = 0; $i -lt 100000; $i++) {
$void = $ranGen.Next()
}
finishes within 0.19 seconds.
I put the call inside a function
Get-RandomNumber {
param( $ranGen )
$ranGen.Next()
}
Executing
for ($i = 0; $i -lt 100000; $i++) {
$void = Get-RandomNumber $ranGen
}
takes about 4 seconds.
Why is there such a huge performance impact?
Is there a way I can use functions and still get the performance I have with the direct call?
Are there better (more performant) ways of code encapsulation in PowerShell?
a function call is expensive. The way to get around that is to put as much as you can IN the function. take a look at the following ...
$ranGen = New-Object System.Random
$RepeatCount = 1e4
'Basic for loop = {0}' -f (Measure-Command -Expression {
for ($i = 0; $i -lt $RepeatCount; $i++) {
$Null = $ranGen.Next()
}
}).TotalMilliseconds
'Core in function = {0}' -f (Measure-Command -Expression {
function Get-RandNum_Core {
param ($ranGen)
$ranGen.Next()
}
for ($i = 0; $i -lt $RepeatCount; $i++) {
$Null = Get-RandNum_Core $ranGen
}
}).TotalMilliseconds
'All in function = {0}' -f (Measure-Command -Expression {
function Get-RandNum_All {
param ($ranGen)
for ($i = 0; $i -lt $RepeatCount; $i++) {
$Null = $ranGen.Next()
}
}
Get-RandNum_All $ranGen
}).TotalMilliseconds
output ...
Basic for loop = 49.4918
Core in function = 701.5473
All in function = 19.5579
from what i vaguely recall [and can't find again], after a certain number of repeats, the function scriptblock gets JIT-ed ... that seems to be where the speed comes from.
I was trying to execute x different server restarts with a PowerShell script, but as soon as it hits my for loop it just ends and I don't know where I made the mistake.
Code:
$x = Read-Host "How much you want to restart"
for ($i=0; $i -eq $x; $i++)
{
$name = read-host "Enter Servername" $i "to restart"
Restart-Computer -ComputerName $name -wait
write-host "Server" $name "restarded"
}
Edit: thx to the answer I corrected the $i++ but still it does end immediately after the number is entered.
arco444's answer should fix your problem. However, maybe it's more interesting to write your code like this:
$serverList = #()
Do {
$name = read-host "Enter Servername to restart"
if ($name) {
$serverList += $name
}
} While ($name)
Foreach ($server in $serverList) {
Restart-Computer -ComputerName $server -wait
write-host "Server $server restarded"
}
This will keep asking for a servername, until you provide none. Then it will go ahead and restart the servers.
Or:
Do {
$name = read-host "Enter Servername to restart"
if ($name) {
Restart-Computer -ComputerName $name -wait
write-host "Server $name restarded"
}
} While ($name)
It might be helpful to encapsulate the rebooting code separately from the gathering of COMPUTERNAMEs. This would make it possible for the list of computers to be created from a script prompting for names or any other method.
When you are confident that the correct computers will be rebooted, remove the -WhatIf from the Restart-Computer cmdlet.
=== Do-Reboot.ps1
[cmdletbinding()]
Param (
[Parameter(Mandatory = $true, Position = 0)]
[string[]]$ComputerName
)
foreach ($Computer in $ComputerName) {
Write-Information "reboot $Computer"
Restart-Computer -ComputerName $Computer -Wait -WhatIf
}
Then, it can be used:
Do-Reboot -ComputerName SERVER1,SERVER2,SERVER3
If you want to see something on the console for each reboot, use:
Do-Reboot -ComputerName SERVER1,SERVER2,SERVER3 -InformationAction Continue
If you have the list of servers in a text file, this could be used.
Do-Reboot -ComputerName $(Get-Content -Path '.\rebootlist.txt')
Use #Michael B.'s answer to improve your code, but below you'll find the explanation why your code doesn't work as expected.
Your script will never enter the for loop due to incorrect condition.
Here you expect to execute the command block when $i is equal to $x:
for ($i=0; $i -eq $x; $i++)
As you initialize $i with the value 0, it'll check that the condition is false and never execute command block.
If you want the command block to be executed x times, your loop should look like:
for ($i=0; $i -lt $x; $i++) {
# command block
}
Alternatively, you can use -ne instead of -lt.
Examples to check:
Working version
$x = 5
for ($i=0; $i -lt$x; $i++) {$i}
Output:
0
1
2
3
4
Non-working version
$x = 5
for ($i=0; $i -eq $x; $i++) {$i}
Output empty
$fullnamexp = ((net user $winxp /domain | Select-String "Full Name") -replace "Full Name","").Trim();
If $winxp cannot be found, the command will hang, is there a timeout I can use with this to make it move on after 5-10 seconds? Not sure where I would put it.
Edit- I use this to pull the username:
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $tag1)
$key = $reg.OpenSubKey('SOFTWARE\Microsoft\Windows NT\CurrentVersion\WinLogon')
$winxp = $key.GetValue('DefaultUserName') -replace '^.*?\\'
$winxp is then a login name such as ajstepanik then I put it into: $fullnamexp = ((net user $winxp /domain | Select-String "Full Name") -replace "Full Name","").Trim();
1.21.2014 Update
$timeoutSeconds = 5
$code = {
((net user $winxp /domain | Select-String "Full Name") -replace "Full Name","").Trim(); # your commands here, e.g.
}
$j = Start-Job -ScriptBlock $code
if (Wait-Job $j -Timeout $timeoutSeconds) { $fullnamexp = Receive-Job $j }
Remove-Job -force $j
While #mjolinor may have indeed provided you an alternative approach, here is a direct answer to your general question: how do you force a timeout in PowerShell?
Wrap whatever you wish to time-limit in a script block, run that as a job, then use the Wait-Job cmdlet to time-limit the operation. Wait-Job will return either at the end of the timeout period or when the script block completes, whichever occurs first. After Wait-Job returns, you can examine the job state ($j.state) to determine whether it was interrupted or not, if it matters to you.
$timeoutSeconds = 5 # set your timeout value here
$j = Start-Job -ScriptBlock {
# your commands here, e.g.
Get-Process
}
"job id = " + $j.id # report the job id as a diagnostic only
Wait-Job $j -Timeout $timeoutSeconds | out-null
if ($j.State -eq "Completed") { "done!" }
elseif ($j.State -eq "Running") { "interrupted" }
else { "???" }
Remove-Job -force $j #cleanup
2014.01.18 Update
Here is a bit more streamlining approach that also includes the practical step of getting information out of the script block with Receive-Job, assuming what you want is generated on stdout:
$timeoutSeconds = 3
$code = {
# your commands here, e.g.
Get-ChildItem *.cs | select name
}
$j = Start-Job -ScriptBlock $code
if (Wait-Job $j -Timeout $timeoutSeconds) { Receive-Job $j }
Remove-Job -force $j
You can use Start-Sleep to pause the script:
Start-Sleep -s 5
net doesn't explicitly allow you to set a time out on it's operations, but you could check out this link on changing the ipv4 timeout for your sockets:
http://www.cyberciti.biz/tips/linux-increasing-or-decreasing-tcp-sockets-timeouts.html
The only thing else I could imagine is spawning a worker thread but I don't even know if that's possible in bash, I'm not fluid enough in it to answer that; plus it opens you up to sync problems and all sorts of multi threaded issues beyond what you're trying to accomplish quickly in a bash script to begin with! :P
Does this help?
$query = (dsquery user -samid $winxp)
if ($query) {$fullnamexp = ($query | dsget user -display)[1].trim()}
$fullnamexp
This solution doesn't work for me. remove-job -force $j takes over 5 seconds in this example.
$timeoutseconds = 1
$start = get-date
$j = start-job -scriptblock { Resolve-DnsName 1.1.1.1 }
if (wait-job $j -timeout $timeoutseconds) { $fullnamexp = receive-job $j }
remove-job -force $j
(get-date) - $start
Days : 0
Hours : 0
Minutes : 0
Seconds : 5
Milliseconds : 342
Ticks : 53426422
TotalDays : 6.18361365740741E-05
TotalHours : 0.00148406727777778
TotalMinutes : 0.0890440366666667
TotalSeconds : 5.3426422
TotalMilliseconds : 5342.6422
Here's a simple timeout example with notepad:
notepad
if (-not $(wait-process notepad 10; $?)) { stop-process -name notepad }
$watchdog = 10 #seconds
$start_time = Get-Date
$j = Start-Job -ScriptBlock{
#timeout command
if ($true) {
$i = 0
while($true) {
Write-Host "Count: $i"
Start-Sleep -Milliseconds 100
$i++
}
}
write-host "Hello"
}
while($true) {
if ($j.HasMoreData) {
Receive-Job $j
Start-Sleep -Milliseconds 200
}
$current = Get-Date
$time_span = $current - $start_time
if ($time_span.TotalSeconds -gt $watchdog) {
write-host "TIMEOUT!"
Stop-Job $j
break
}
if (-not $j.HasMoreData -and $j.State -ne 'Running') {
write-host "Finished"
break
}
}
Remove-Job $j