@@ -225,8 +225,8 @@ function cache_path($app, $version, $url) { "$cachedir\$app#$version#$($url -rep
225
225
226
226
# apps
227
227
function sanitary_path ($path ) { return [regex ]::replace($path , " [/\\?:*<>|]" , " " ) }
228
- function installed ($app , $global ) {
229
- if (-not $PSBoundParameters .ContainsKey ( ' global' ) ) {
228
+ function installed ($app , [ Nullable [ bool ]] $global ) {
229
+ if ($null -eq $ global ) {
230
230
return (installed $app $false ) -or (installed $app $true )
231
231
}
232
232
# Dependencies of the format "bucket/dependency" install in a directory of form
@@ -507,34 +507,63 @@ function Invoke-ExternalCommand {
507
507
}
508
508
$Process = New-Object System.Diagnostics.Process
509
509
$Process.StartInfo.FileName = $FilePath
510
- $Process.StartInfo.Arguments = ($ArgumentList | Select-Object - Unique) -join ' '
511
510
$Process.StartInfo.UseShellExecute = $false
512
511
if ($LogPath ) {
513
- if ($FilePath -match ' (^|\W) msiexec($|\W) ' ) {
514
- $Process .StartInfo.Arguments += " /lwe `" $LogPath `" "
512
+ if ($FilePath -match ' ^ msiexec(.exe)?$ ' ) {
513
+ $ArgumentList += " /lwe `" $LogPath `" "
515
514
} else {
515
+ $redirectToLogFile = $true
516
516
$Process.StartInfo.RedirectStandardOutput = $true
517
517
$Process.StartInfo.RedirectStandardError = $true
518
518
}
519
519
}
520
520
if ($RunAs ) {
521
521
$Process.StartInfo.UseShellExecute = $true
522
522
$Process.StartInfo.Verb = ' RunAs'
523
+ } else {
524
+ $Process.StartInfo.CreateNoWindow = $true
525
+ }
526
+ if ($FilePath -match ' ^((cmd|cscript|wscript|msiexec)(\.exe)?|.*\.(bat|cmd|js|vbs|wsf))$' ) {
527
+ $Process.StartInfo.Arguments = $ArgumentList -join ' '
528
+ } elseif ($Process.StartInfo.ArgumentList.Add ) {
529
+ # ArgumentList is supported in PowerShell 6.1 and later (built on .NET Core 2.1+)
530
+ # ref-1: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.processstartinfo.argumentlist?view=net-6.0
531
+ # ref-2: https://docs.microsoft.com/en-us/powershell/scripting/whats-new/differences-from-windows-powershell?view=powershell-7.2#net-framework-vs-net-core
532
+ $ArgumentList | ForEach-Object { $Process.StartInfo.ArgumentList.Add ($_ ) }
533
+ } else {
534
+ # escape arguments manually in lower versions, refer to https://docs.microsoft.com/en-us/previous-versions/17w5ykft(v=vs.85)
535
+ $escapedArgs = $ArgumentList | ForEach-Object {
536
+ # escape N consecutive backslash(es), which are followed by a double quote, to 2N consecutive ones
537
+ $s = $_ -replace ' (\\+)"' , ' $1$1"'
538
+ # escape N consecutive backslash(es), which are at the end of the string, to 2N consecutive ones
539
+ $s = $s -replace ' (\\+)$' , ' $1$1'
540
+ # escape double quotes
541
+ $s = $s -replace ' "' , ' \"'
542
+ # quote the argument
543
+ " `" $s `" "
544
+ }
545
+ $Process.StartInfo.Arguments = $escapedArgs -join ' '
523
546
}
524
547
try {
525
- $Process.Start () | Out-Null
548
+ [ void ] $Process.Start ()
526
549
} catch {
527
550
if ($Activity ) {
528
551
Write-Host " error." - ForegroundColor DarkRed
529
552
}
530
553
error $_.Exception.Message
531
554
return $false
532
555
}
533
- if ($LogPath -and ($FilePath -notmatch ' (^|\W)msiexec($|\W)' )) {
534
- Out-UTF8File - FilePath $LogPath - Append - InputObject $Process.StandardOutput.ReadToEnd ()
535
- Out-UTF8File - FilePath $LogPath - Append - InputObject $Process.StandardError.ReadToEnd ()
556
+ if ($redirectToLogFile ) {
557
+ # we do this to remove a deadlock potential
558
+ # ref: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.process.standardoutput?view=netframework-4.5#remarks
559
+ $stdoutTask = $Process.StandardOutput.ReadToEndAsync ()
560
+ $stderrTask = $Process.StandardError.ReadToEndAsync ()
536
561
}
537
562
$Process.WaitForExit ()
563
+ if ($redirectToLogFile ) {
564
+ Out-UTF8File - FilePath $LogPath - Append - InputObject $stdoutTask.Result
565
+ Out-UTF8File - FilePath $LogPath - Append - InputObject $stderrTask.Result
566
+ }
538
567
if ($Process.ExitCode -ne 0 ) {
539
568
if ($ContinueExitCodes -and ($ContinueExitCodes.ContainsKey ($Process.ExitCode ))) {
540
569
if ($Activity ) {
@@ -604,12 +633,12 @@ function movedir($from, $to) {
604
633
$proc.StartInfo.RedirectStandardError = $true
605
634
$proc.StartInfo.UseShellExecute = $false
606
635
$proc.StartInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle ]::Hidden
607
- $proc.Start ()
608
- $out = $proc.StandardOutput.ReadToEnd ()
636
+ [ void ] $proc.Start ()
637
+ $stdoutTask = $proc.StandardOutput.ReadToEndAsync ()
609
638
$proc.WaitForExit ()
610
639
611
640
if ($proc.ExitCode -ge 8 ) {
612
- debug $out
641
+ debug $stdoutTask .Result
613
642
throw " Could not find '$ ( fname $from ) '! (error $ ( $proc.ExitCode ) )"
614
643
}
615
644
0 commit comments