Private/Resolve-BsnScriptInvocation.ps1
|
<#
.SYNOPSIS Builds the script(1) arguments for a timing-based recording, probing the installed binary for invocation compatibility. .DESCRIPTION util-linux changed script(1) invocation compatibility across releases: - util-linux 2.39+ (Ubuntu 24.04, 26.04) REJECTS combining --log-out with a trailing positional typescript file ('unexpected number of arguments') - the recording process exits immediately and every cast comes back empty. - util-linux 2.37 (Ubuntu 22.04) and older releases with --log-timing accept both shapes. This helper probes the installed binary once with a no-op child using the classic positional form: where it is accepted, the historically tested invocation is kept; where it is rejected, the options-only form is used. No version string parsing is involved, so any future util-linux release is handled by the same probe. Kept private (invoked by the Start-InteractiveTranscript engine). #> function Resolve-BsnScriptInvocation { [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory = $true)] [string]$ScriptPath, [Parameter(Mandatory = $true)] [string]$Command, [Parameter(Mandatory = $true)] [string]$TimingPath, [Parameter(Mandatory = $true)] [string]$OutputPath ) $probeTimingPath = [System.IO.Path]::GetTempFileName() $probeOutputPath = [System.IO.Path]::GetTempFileName() try { $probeInfo = [System.Diagnostics.ProcessStartInfo]::new() $probeInfo.FileName = $ScriptPath $probeInfo.UseShellExecute = $false $probeInfo.RedirectStandardError = $true $probeInfo.Arguments = ('-q -f -e -c "true" --log-timing "{0}" --log-out "{1}" /dev/null' -f $probeTimingPath, $probeOutputPath) $probeProcess = [System.Diagnostics.Process]::Start($probeInfo) if (-not $probeProcess.WaitForExit(10000)) { $probeProcess.Kill() Write-Verbose -Message 'script(1) invocation probe timed out; using the options-only form.' return ('-q -f -e -c "{0}" --log-timing "{1}" --log-out "{2}"' -f $Command, $TimingPath, $OutputPath) } # Drain stderr so the probe's rejection message never surfaces to the user. $null = $probeProcess.StandardError.ReadToEnd() if ($probeProcess.ExitCode -eq 0) { Write-Verbose -Message 'script(1) accepts the positional typescript file; using the classic invocation.' return ('-q -f -e -c "{0}" --log-timing "{1}" --log-out "{2}" /dev/null' -f $Command, $TimingPath, $OutputPath) } Write-Verbose -Message ('script(1) rejected the positional typescript file (exit {0}); using the options-only form.' -f $probeProcess.ExitCode) return ('-q -f -e -c "{0}" --log-timing "{1}" --log-out "{2}"' -f $Command, $TimingPath, $OutputPath) } finally { Remove-Item -LiteralPath $probeTimingPath, $probeOutputPath -Force -ErrorAction 'SilentlyContinue' } } |