Last active
March 29, 2025 10:35
-
-
Save scriptingstudio/1796ba348710983d985e2836f47b9314 to your computer and use it in GitHub Desktop.
Yet another way to run timed Powershell commands and scripts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Invoke-PSCommand { | |
[CmdletBinding(DefaultParameterSetName='scriptblock')] | |
[alias('ipsc')] | |
param ( | |
[Parameter(Mandatory,Position=0,ParameterSetName='scriptblock')] | |
[ValidateNotNullOrEmpty()] | |
[alias('sb')][ScriptBlock] $ScriptBlock, | |
[Parameter(Mandatory,Position=0,ParameterSetName='file')] | |
[ValidateNotNullOrEmpty()] | |
[alias('fn')][string] $FileName, | |
[Parameter(Position=1)] | |
[alias('timeout','to')][int] $OperationTimeoutSec, | |
[string[]] $Variables, | |
[string[]] $Functions, | |
[string[]] $Switches, | |
[hashtable] $Parameters = @{}, | |
[switch] $Core, # experimental | |
[switch] $Quiet | |
) | |
if ($Quiet) { | |
$ErrorActionPreference = 'SilentlyContinue' | |
$WarningPreference = 'SilentlyContinue' | |
} | |
# Adjust defaults | |
if ($FileName) { | |
$ScriptBlock = [ScriptBlock]::Create((Get-Content $FileName -Raw -ErrorAction $ErrorActionPreference)) | |
if ($Quiet -and -not $ScriptBlock) {return} | |
} | |
if ($ScriptBlock.ToString().Length -eq 0) {return} # nothing to do | |
if ($Parameters -eq $null) {$Parameters = @{}} | |
# "using:" variables | |
$usingParams = @{} | |
$ast = $ScriptBlock.Ast.FindAll({$args[0] -is [System.Management.Automation.Language.UsingExpressionAst]}, $true) | |
foreach ($usingstatement in $ast) { | |
$varText = $usingstatement.Extent.Text | |
$varPath = $usingstatement.SubExpression.VariablePath.UserPath | |
$key = [Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($varText.ToLower())) | |
if (-not $usingParams.ContainsKey($key)) { | |
$usingParams.Add($key, $PSCmdlet.SessionState.PSVariable.GetValue($varPath)) | |
} | |
} | |
if ($usingParams.Count) { | |
$Parameters['--%'] = $usingParams | |
} | |
# Init session | |
$iss = if ($core) {[InitialSessionState]::CreateDefault2()} else {[InitialSessionState]::CreateDefault()} | |
# import variables | |
foreach ($v in $Variables) { | |
if (-not $v) {continue} | |
$iss.Variables.Add([System.Management.Automation.Runspaces.SessionStateVariableEntry]::new($v, (Get-Variable $v -ErrorAction 0).value, $null)) | |
} | |
# import functions | |
foreach ($f in $Functions) { | |
if (-not $f) {continue} | |
$def = (Get-Command $f -ErrorAction 0).Definition | |
if ($def) { | |
$iss.Commands.Add([System.Management.Automation.Runspaces.SessionStateFunctionEntry]::new($f, $def)) | |
} | |
} | |
$Runspace = [runspacefactory]::CreateRunspace($iss) | |
$PowerShell = [powershell]::Create() | |
$null = $PowerShell.AddScript($ScriptBlock) | |
if ($Parameters.Count) {$null = $PowerShell.AddParameters($Parameters)} | |
$PowerShell.Runspace = $Runspace | |
# Run session | |
$Runspace.Open() | |
if ($OperationTimeoutSec -gt 0) { | |
$IAsyncResult = $PowerShell.BeginInvoke() # start job | |
$timeoutms = $OperationTimeoutSec * 1000 - 150 | |
$timer = [System.Diagnostics.Stopwatch]::StartNew() # job timer | |
do {[System.Threading.Thread]::Sleep(150)} # polling interval | |
until ($IAsyncResult.IsCompleted -or $timeoutms -lt $timer.Elapsed.TotalMilliseconds) | |
if ($IAsyncResult.IsCompleted) { # success: just in time | |
$PowerShell.EndInvoke($IAsyncResult) # get results | |
} elseif (-not $Quiet) { | |
Write-Warning "Run out of time" # Session timer has expired | |
} | |
$timer.Stop() | |
} else { | |
$PowerShell.Invoke() # get results | |
} | |
# show errors | |
if (-not $Quiet -and $PowerShell.HadErrors) { | |
foreach ($e in $PowerShell.Streams.Error) { | |
$PSCmdlet.WriteError($e) | |
} | |
} | |
# Cleanup session | |
$PowerShell.Stop() | |
$Runspace.Close() | |
$Runspace.Dispose() | |
$PowerShell.Dispose() | |
} # END Invoke-PSCommand | |
$timeout = 2 | |
$pname = 'dllhost' | |
Invoke-PSCommand {Start-Sleep -milliSeconds 2200; Get-Process -Name $using:pname; fakecmd} $timeout |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment