|
| 1 | +<# |
| 2 | +.SYNOPSIS |
| 3 | +Caches the output of a command for recall if called again. |
| 4 | +
|
| 5 | +.FUNCTIONALITY |
| 6 | +Command |
| 7 | +
|
| 8 | +.EXAMPLE |
| 9 | +Get-CachedCommand.ps1 {Invoke-RestMethod https://example.org/endpoint} -Session |
| 10 | +
|
| 11 | +Returns the result of executing the script block, or the previous cached output if available. |
| 12 | +#> |
| 13 | + |
| 14 | +#Requires -Version 7 |
| 15 | +[CmdletBinding()] Param( |
| 16 | +# Specifies the expression to cache the output of. |
| 17 | +[Parameter(Position=0,Mandatory=$true)][scriptblock] $Expression, |
| 18 | +# Parameters to the script block. |
| 19 | +[Parameter(Position=1,ValueFromRemainingArguments=$true)][psobject[]] $BlockArgs = @(), |
| 20 | +# The rolling duration to cache the output for (updated with each call). |
| 21 | +[Parameter(ParameterSetName='ExpiresAfter')][timespan] $ExpiresAfter, |
| 22 | +# Point in time to cache the output until. |
| 23 | +[Parameter(ParameterSetName='Expires')][datetime] $Expires, |
| 24 | +# Caches the output for this session. |
| 25 | +[Parameter(ParameterSetName='Session')][switch] $Session |
| 26 | +) |
| 27 | +Begin |
| 28 | +{ |
| 29 | + $Script:Hash = [Security.Cryptography.MD5]::Create() |
| 30 | + function Get-ScriptBlockHash |
| 31 | + { |
| 32 | + [CmdletBinding()] Param( |
| 33 | + [Parameter(Position=0)][scriptblock] $ScriptBlock, |
| 34 | + [Parameter(Position=1)][psobject[]] $BlockArgs = @() |
| 35 | + ) |
| 36 | + return (@("$ScriptBlock") + @($BlockArgs |ForEach-Object {ConvertTo-Json $_ -Compress}) | |
| 37 | + ForEach-Object {ConvertTo-Base64.ps1 $Script:Hash.ComputeHash([Text.Encoding]::UTF8.GetBytes($_)) -UriStyle}) -join '.' |
| 38 | + } |
| 39 | + |
| 40 | + $Script:SessionCacheName = "$(Split-Path $PSCommandPath -LeafBase)#$PID" |
| 41 | + $Script:SessionCache = Get-Variable $Script:SessionCacheName -Scope Global -ErrorAction Ignore |
| 42 | + if(!$Script:SessionCache) |
| 43 | + { |
| 44 | + $Script:SessionCache = Set-Variable $Script:SessionCacheName @{} -Scope Global -Option Constant -PassThru ` |
| 45 | + -Description 'Cached command output' |
| 46 | + } |
| 47 | + $Script:CacheDir = if($IsWindows) |
| 48 | + { |
| 49 | + Join-Path $env:LocalAppData (Split-Path $PSCommandPath -LeafBase) |
| 50 | + } |
| 51 | + elseif($IsLinux) |
| 52 | + { |
| 53 | + Join-Path ($env:XDG_CACHE_HOME ?? "$HOME/.cache") (Split-Path $PSCommandPath -LeafBase) |
| 54 | + } |
| 55 | + else |
| 56 | + { |
| 57 | + Join-Path $env:TEMP (Split-Path $PSCommandPath -LeafBase) |
| 58 | + } |
| 59 | + New-Item $Script:CacheDir -Type Directory -ErrorAction Ignore |Out-Null |
| 60 | + Get-ChildItem $Script:CacheDir | |
| 61 | + Where-Object LastWriteTime -lt (Get-Date) | |
| 62 | + Remove-Item |
| 63 | + function Get-CachedOutput |
| 64 | + { |
| 65 | + [CmdletBinding()] Param( |
| 66 | + [Parameter(Position=0)][scriptblock] $ScriptBlock, |
| 67 | + [Parameter(Position=1)][psobject[]] $BlockArgs = @(), |
| 68 | + [datetime] $Expires |
| 69 | + ) |
| 70 | + $now = Get-Date |
| 71 | + $cacheFileName = Join-Path $Script:CacheDir "$(Get-ScriptBlockHash $ScriptBlock).xml" |
| 72 | + if(Test-Path $cacheFileName -Type Leaf) |
| 73 | + { |
| 74 | + $cacheFile = Get-Item $cacheFileName |
| 75 | + if($now -gt $cacheFile.LastWriteTime) |
| 76 | + { |
| 77 | + $ScriptBlock.InvokeReturnAsIs($BlockArgs) |Export-Clixml $cacheFileName -Force |
| 78 | + } |
| 79 | + } |
| 80 | + else |
| 81 | + { |
| 82 | + $ScriptBlock.InvokeReturnAsIs($BlockArgs) |Export-Clixml $cacheFileName |
| 83 | + $cacheFile = Get-Item $cacheFileName |
| 84 | + } |
| 85 | + $cacheFile.LastWriteTime = $now |
| 86 | + Import-Clixml $cacheFileName |
| 87 | + } |
| 88 | +} |
| 89 | +Process |
| 90 | +{ |
| 91 | + switch($PSCmdlet.ParameterSetName) |
| 92 | + { |
| 93 | + ExpiresAfter {Get-CachedOutput $Expression -BlockArgs $BlockArgs -Expires (Get-Date).Add($ExpiresAfter)} |
| 94 | + ExpiresAfter {Get-CachedOutput $Expression -BlockArgs $BlockArgs -Expires $Expires} |
| 95 | + Session |
| 96 | + { |
| 97 | + $key = Get-ScriptBlockHash $Expression -BlockArgs $BlockArgs |
| 98 | + if(!$Script:SessionCache.Value.ContainsKey($key)) |
| 99 | + { |
| 100 | + $Script:SessionCache.Value[$key] = $Expression.InvokeReturnAsIs($BlockArgs) |
| 101 | + } |
| 102 | + return $Script:SessionCache.Value[$key] |
| 103 | + } |
| 104 | + } |
| 105 | +} |
0 commit comments