diff --git a/PSOneTools/1.3/Foreach-ObjectFast.ps1 b/PSOneTools/1.3/Foreach-ObjectFast.ps1 new file mode 100644 index 0000000..757c996 --- /dev/null +++ b/PSOneTools/1.3/Foreach-ObjectFast.ps1 @@ -0,0 +1,102 @@ +function Foreach-ObjectFast +{ + <# + .SYNOPSIS + Faster Foreach-Object + + .DESCRIPTION + Foreach-ObjectFast can replace the built-in Foreach-Object and improves pipeline speed considerably. + Foreach-ObjectFast supports only the most commonly used parameters -Begin, -Process, and -End, so you can replace + + 1..100 | Foreach-Object { 'Server{0:d3}' -f $_ } + + with + + 1..100 | Foreach-ObjectFast { 'Server{0:d3}' -f $_ } + + but you cannot currently replace instances of Foreach-Object that uses the less commonly used parameters, + like -RemainingScripts, -MemberNames, and -ArgumentList + + Foreach-ObjectFast has a performance benefit per iteration, so the more objects + you send through the pipeline, the more significant performace benefits you will see. + + Foreach-ObjectFast is using a steppable pipeline internally which performs better. + However because of this, the debugging experience will be different, and internal + variables such as $MyInvocation may yield different results. For most every-day tasks, + these changes are not important. + + A complete explanation of what Where-ObjectFast does can be found here: + https://powershell.one/tricks/performance/pipeline + + .EXAMPLE + $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() + + $result = 1..1000000 | Foreach-ObjectFast -Process { + "I am at $_" + } + + $report = '{0} elements in {1:n2} seconds' + $report -f $result.Count, $stopwatch.Elapsed.TotalSeconds + + Demos the speed improvements. Run this script to see how well it performs, + then replace Foreach-ObjectFast with the default Foreach-Object, and check out + the performace difference. $result is the same in both cases. + + .LINK + https://powershell.one/tricks/performance/pipeline + https://github.com/TobiasPSP/Modules.PSOneTools/blob/master/PSOneTools/1.2/Foreach-ObjectFast.ps1 + #> + + param + ( + # executes for each pipeline element + [ScriptBlock] + $Process, + + # executes once before the pipeline is started. + # can be used for initialization routines + [ScriptBlock] + $Begin, + + # executes once after all pipeline elements have been processed + # can be used to do cleanup work + [ScriptBlock] + $End + ) + + begin + { + # construct a hard-coded anonymous simple function from + # the submitted scriptblocks: + $code = @" +& { + begin + { + $Begin + } + process + { + $Process + } + end + { + $End + } +} +"@ + # turn code into a scriptblock and invoke it + # via a steppable pipeline so we can feed in data + # as it comes in via the pipeline: + $pip = [ScriptBlock]::Create($code).GetSteppablePipeline($myInvocation.CommandOrigin) + $pip.Begin($true) + } + process + { + # forward incoming pipeline data to the custom scriptblock: + $pip.Process($_) + } + end + { + $pip.End() + } +} \ No newline at end of file diff --git a/PSOneTools/1.3/PSOneTools.psd1 b/PSOneTools/1.3/PSOneTools.psd1 new file mode 100644 index 0000000..95e60b5 Binary files /dev/null and b/PSOneTools/1.3/PSOneTools.psd1 differ diff --git a/PSOneTools/1.3/Test-PSOnePing.ps1 b/PSOneTools/1.3/Test-PSOnePing.ps1 new file mode 100644 index 0000000..83ba673 --- /dev/null +++ b/PSOneTools/1.3/Test-PSOnePing.ps1 @@ -0,0 +1,60 @@ + +function Test-PSOnePing +{ + <# + .SYNOPSIS + Sends a ping (ICMP) to a computer + + .DESCRIPTION + Sends a ping (ICMP) to a computer + + .EXAMPLE + Test-PSOnePing -ComputerName 127.0.0.1, microsoft.com, powershell.one -Timeout 2000 + Pings three computers with a maximum timeout of 2000 milliseconds + + .EXAMPLE + '127.0.0.1', 'microsoft.com', 'powershell.one' | Test-PSOnePing -Timeout 2000 + Pings three computers with a maximum timeout of 2000 milliseconds + + .EXAMPLE + Get-ADComputer -Filter * | Select-Object -ExpandProperty Name | Test-PSOnePing -Timeout 2000 + Pings all computers received from Get-ADComputer with a maximum timeout of 2000 milliseconds + Module "ActiveDirectory" required for Get-ADComputer + + .LINK + https://powershell.one/tricks/network/ping + #> + + + param + ( + # Computername or IP address to ping + [Parameter(Mandatory,ValueFromPipeline)] + [string[]] + $ComputerName, + + # Timeout in milliseconds + [int] + [ValidateRange(100,50000)] + $Timeout = 2000 + ) + + begin + { + $Online = @{ + Name = 'Online' + Expression = { $_.Status -eq 'Success' } + } + $obj = New-Object System.Net.NetworkInformation.Ping + } + + process + { + $ComputerName | + ForEach-Object { + $obj.Send($_, $timeout) | + Select-Object -Property $Online, Status, Address | + Add-Member -MemberType NoteProperty -Name Name -Value $_ -PassThru + } + } +} \ No newline at end of file diff --git a/PSOneTools/1.3/Test-PSOnePort.ps1 b/PSOneTools/1.3/Test-PSOnePort.ps1 new file mode 100644 index 0000000..58bed65 --- /dev/null +++ b/PSOneTools/1.3/Test-PSOnePort.ps1 @@ -0,0 +1,126 @@ +function Test-PSOnePort +{ + <# + .SYNOPSIS + Tests a network port on a remote computer + + .DESCRIPTION + Tests whether a port on a remote computer is responding. + + .EXAMPLE + Test-PSOnePort -ComputerName 127.0.0.1 -Port 4000 -Timeout 1000 + Tests whether port 4000 on the local computer is responding, + and waits a maximum of 1000 milliseconds + + .EXAMPLE + Test-PSOnePort -ComputerName 127.0.0.1 -Port 4000 -Timeout 1000 -Count 30 -Delay 2000 + Tests 30 times whether port 4000 on the local computer is responding, + and waits a maximum of 1000 milliseconds inbetween each test + + .EXAMPLE + Test-PSOnePort -ComputerName 127.0.0.1 -Port 4000 -Timeout 1000 -Count 0 -Delay 2000 -ExitOnSuccess + Continuously tests whether port 4000 on the local computer is responding, + waits a maximum of 1000 milliseconds inbetween each test, + and exits as soon as the port is responding + + .LINK + https://powershell.one/tricks/network/porttest + #> + + + param + ( + [Parameter(Mandatory)] + [string] + $ComputerName, + + # port number to test + [Parameter(Mandatory)] + [int] + $Port, + + # timeout in milliseconds + [int] + $Timeout = 500, + + # number of tries. A value of 0 indicates countinuous testing + [int] + [ValidateRange(0,1000)] + $Count = 1, + + # delay (in milliseconds) inbetween continuous tests + $Delay = 2000, + + # when enabled, function returns as soon as port is available + [Switch] + $ExitOnSuccess + ) + $ok = $false + $c = 0 + $isOnline = $false + $continuous = $Count -eq 0 -or $Count -gt 1 + try + { + do + { + $c++ + if ($c -gt $Count -and !$continuous) { + # count exceeded + break + } + $start = Get-Date + + $tcpobject = [system.Net.Sockets.TcpClient]::new() + $connect = $tcpobject.BeginConnect($computername,$port,$null,$null) + $wait = $connect.AsyncWaitHandle.WaitOne($timeout,$false) + + if(!$wait) { + # no response from port + $tcpobject.Close() + $tcpobject.Dispose() + + Write-Verbose "Port $Port is not responding..." + if ($continuous) { Write-Host '.' -NoNewline } + } else { + try { + # port is reachable + if ($continuous) { Write-Host '!' -NoNewline } + [void]$tcpobject.EndConnect($connect) + $tcpobject.Close() + $tcpobject.Dispose() + + $isOnline = $true + if ($ExitOnSuccess) + { + $ok = $true + $delay = 0 + } + } + catch { + # access to port restricted + throw "You do not have permission to contact port $Port." + } + } + $stop = Get-Date + $timeUsed = ($stop - $start).TotalMilliseconds + $currentDelay = $Delay - $timeUsed + if ($currentDelay -gt 100) + { + Start-Sleep -Milliseconds $currentDelay + } + + } until ($ok) + } + finally + { + # dispose objects to free memory + if ($tcpobject) + { + $tcpobject.Close() + $tcpobject.Dispose() + } + } + if ($continuous) { Write-Host } + + return $isOnline +} \ No newline at end of file diff --git a/PSOneTools/1.3/Test-PSOneScript.ps1 b/PSOneTools/1.3/Test-PSOneScript.ps1 new file mode 100644 index 0000000..b4e92d7 --- /dev/null +++ b/PSOneTools/1.3/Test-PSOneScript.ps1 @@ -0,0 +1,66 @@ + + +function Test-PSOneScript +{ + <# + .SYNOPSIS + Parses a PowerShell Script (*.ps1, *.psm1, *.psd1) + + .DESCRIPTION + Invokes the simple PSParser and returns tokens and syntax errors + + .EXAMPLE + Test-PSOneScript -Path c:\test.ps1 + Parses the content of c:\test.ps1 and returns tokens and syntax errors + + .EXAMPLE + Get-ChildItem -Path $home -Recurse -Include *.ps1,*.psm1,*.psd1 -File | + Test-PSOneScript | + Out-GridView + + parses all PowerShell files found anywhere in your user profile + + .EXAMPLE + Get-ChildItem -Path $home -Recurse -Include *.ps1,*.psm1,*.psd1 -File | + Test-PSOneScript | + Where-Object Errors + + parses all PowerShell files found anywhere in your user profile + and returns only those files that contain syntax errors + + .LINK + https://powershell.one + #> + + + param + ( + # Path to PowerShell script file + # can be a string or any object that has a "Path" + # or "FullName" property: + [String] + [Parameter(Mandatory,ValueFromPipeline)] + [Alias('FullName')] + $Path + ) + + begin + { + $errors = $null + } + process + { + # create a variable to receive syntax errors: + $errors = $null + # tokenize PowerShell code: + $code = Get-Content -Path $Path -Raw -Encoding Default + + # return the results as a custom object + [PSCustomObject]@{ + Name = Split-Path -Path $Path -Leaf + Path = $Path + Tokens = [Management.Automation.PSParser]::Tokenize($code, [ref]$errors) + Errors = $errors | Select-Object -ExpandProperty Token -Property Message + } + } +} \ No newline at end of file diff --git a/PSOneTools/1.3/Where-ObjectFast.ps1 b/PSOneTools/1.3/Where-ObjectFast.ps1 new file mode 100644 index 0000000..d8db361 --- /dev/null +++ b/PSOneTools/1.3/Where-ObjectFast.ps1 @@ -0,0 +1,87 @@ +function Where-ObjectFast +{ + <# + .SYNOPSIS + Faster Where-Object + + .DESCRIPTION + Where-ObjectFast can replace the built-in Where-Object and improves pipeline speed considerably. + Where-ObjectFast supports only the scriptblock version of Where-Object, so you can replace + + Get-Service | Where-Object { $_.Status -eq 'Running' } + + with + + Get-Service | Where-ObjectFast { $_.Status -eq 'Running' } + + but you cannot currently replace the short form of Where-Object: + + Get-Service | Where-Object Status -eq Running + + Where-ObjectFast has a performance benefit per iteration, so the more objects + you send through the pipeline, the more significant performace benefits you will see. + + Where-ObjectFast is using a steppable pipeline internally which performs better. + However because of this, the debugging experience will be different, and internal + variables such as $MyInvocation may yield different results. For most every-day tasks, + these changes are not important. + + A complete explanation of what Where-ObjectFast does can be found here: + https://powershell.one/tricks/performance/pipeline + + .EXAMPLE + $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() + + $result = 1..1000000 | Where-ObjectFast -FilterScript { + $_ % 5 + } + + $report = '{0} elements in {1:n2} seconds' + $report -f $result.Count, $stopwatch.Elapsed.TotalSeconds + + Demos the speed improvements. Run this script to see how well it performs, + then replace Where-ObjectFast with the default Where-Object, and check out + the performace difference. $result is the same in both cases. + + .LINK + https://powershell.one/tricks/performance/pipeline + https://github.com/TobiasPSP/Modules.PSOneTools/blob/master/PSOneTools/1.2/Where-ObjectFast.ps1 + #> + + + param + ( + # Filter scriptblock that is applied to each pipeline element. + # When the filter scriptblock evaluates to $true, the element can pass, + # else the element is filtered out. + [ScriptBlock] + $FilterScript + ) + + begin + { + # construct a hard-coded anonymous simple function: + $code = @" +& { + process { + if ($FilterScript) + { `$_ } + } +} +"@ + # turn code into a scriptblock and invoke it + # via a steppable pipeline so we can feed in data + # as it comes in via the pipeline: + $pip = [ScriptBlock]::Create($code).GetSteppablePipeline($myInvocation.CommandOrigin) + $pip.Begin($true) + } + process + { + # forward incoming pipeline data to the custom scriptblock: + $pip.Process($_) + } + end + { + $pip.End() + } +} \ No newline at end of file diff --git a/PSOneTools/1.3/init.ps1 b/PSOneTools/1.3/init.ps1 new file mode 100644 index 0000000..680b686 --- /dev/null +++ b/PSOneTools/1.3/init.ps1 @@ -0,0 +1,7 @@ + +# use this file to define global variables on module scope +# or perform other initialization procedures. +# this file will not be touched when new functions are exported to +# this module. + + diff --git a/PSOneTools/1.3/module.psm1 b/PSOneTools/1.3/module.psm1 new file mode 100644 index 0000000..7c929b3 --- /dev/null +++ b/PSOneTools/1.3/module.psm1 @@ -0,0 +1,16 @@ +# DO NOT MODIFY THIS FILE! +# THIS FILE WAS AUTOGENERATED BY ISESTEROIDS AND WILL BE OVERWRITTEN WHEN YOU EXPORT FUNCTIONS TO THIS MODULE. + +# USE THIS FILE FOR ADDITIONAL MODULE CODE. THIS FILE WILL NOT BE OVERWRITTEN +# WHEN NEW CONTENT IS PUBLISHED TO THIS MODULE: +. $PSScriptRoot\init.ps1 + + +# LOADING ALL FUNCTION DEFINITIONS: + +. $PSScriptRoot\Test-PSOnePort.ps1 +. $PSScriptRoot\Test-PSOnePing.ps1 +. $PSScriptRoot\Foreach-ObjectFast.ps1 +. $PSScriptRoot\Where-ObjectFast.ps1 +. $PSScriptRoot\Test-PSOneScript.ps1 +