Powershell - redirect executable's stderr to file or variable but still have stdout go to console -
i'm writing script download several repositories github. here command download repo:
git clone "$repositoryurl" "$localrepodirectory" when run command displays nice progress info in console window want displayed.
the problem want able detect if errors have occurred while downloading. found this post talks redirecting various streams, tried:
(git clone "$repositoryurl" "$localrepodirectory") 2> $errorlogfilepath this pipes errors stderr file, no longer displays nice progress info in console.
i can use tee-object so:
(git clone "$repositoryurl" "$localrepodirectory") | tee-object -filepath $errorlogfilepath and still nice progress output, pipes stdout file, not stderr; i'm concerned detecting errors.
is there way can store errors occur in file or (preferably) variable, while still having progress info piped console window? have feeling answer might lie in redirecting various streams other streams discusses in post, i'm not sure.
any suggestions appreciated. thanks!
======== update =======
i'm not sure if git.exe different typical executable, i've done more testing , here i've found:
$output = (git clone "$repositoryurl" "$localrepodirectory") $output contains text "cloning '[localrepodirectory]'...", whether command completed or produced error. also, progress info still written console when doing this. leads me think progress info not written via stdout, other stream?
if error occurs error written console, in usual white foreground color, not typical red errors , yellow warnings. when called within cmdlet function , command fails error, error not returned via function's -errorvariable (or -warningvariable) parameter (however if own write-error returned via -errorvariable). leads me think git.exe doesn't write stderr, when do:
(git clone "$repositoryurl" "$localrepodirectory") 2> $errorlogfilepath the error message written file, makes me think write stderr. i'm confused...
======== update 2 =======
so byron's i've tried couple more solutions using new process, still can't want. when using new process never nice progress written console.
the 3 new methods i've tried both use bit of code in common:
$process = new-object system.diagnostics.process $process.startinfo.arguments = "clone ""$repositoryurl"" ""$localrepodirectory""" $process.startinfo.useshellexecute = $false $process.startinfo.redirectstandardoutput = $true $process.startinfo.redirectstandarderror = $true $process.startinfo.createnowindow = $true $process.startinfo.workingdirectory = $working_directory $process.startinfo.filename = "git" method 1 - run in new process , read output aftewards:
$process.start() $process.waitforexit() write-host output - $process.standardoutput.readtoend() write-host errors - $process.standarderror.readtoend() method 2 - output synchronously:
$process.start() while (!$process.hasexited) { write-host output - $process.standardoutput.readtoend() write-host error output - $process.standarderror.readtoend() start-sleep -seconds 1 } even though looks write output while process running, doesn't write until after process exits.
method 3 - output asynchronously:
register-objectevent -inputobject $process -eventname "outputdatareceived" -action {write-host output data - $args[1].data } register-objectevent -inputobject $process -eventname "errordatareceived" -action { write-host error data - $args[1].data } $process.start() $process.beginoutputreadline() $process.beginerrorreadline() while (!$process.hasexited) { start-sleep -seconds 1 } this output data while process working good, still doesn't display nice progress info :(
i think have answer. i'm working powershell while , created several build systems. sorry if script bit long, works.
$dir = <your dir> $global:log = <your log file must in global scope> # not global = won't work function create-process { $process = new-object -typename system.diagnostics.process $process.startinfo.createnowindow = $false $process.startinfo.redirectstandarderror = $true $process.startinfo.useshellexecute = $false return $process } function terminate-process { param([system.diagnostics.process]$process) $code = $process.exitcode $process.close() $process.dispose() remove-variable process return $code } function launch-process { param([system.diagnostics.process]$process, [string]$log, [int]$timeout = 0) $errorjob = register-objectevent -inputobject $process -eventname errordatareceived -sourceidentifier common.launchprocess.error -action { if(-not [string]::isnullorempty($eventargs.data)) { "error - $($eventargs.data)" | out-file $log -encoding ascii -append write-host "error - $($eventargs.data)" } } $outputjob = register-objectevent -inputobject $process -eventname outputdatareceived -sourceidentifier common.launchprocess.output -action { if(-not [string]::isnullorempty($eventargs.data)) { "out - $($eventargs.data)" | out-file $log -encoding ascii -append write-host "out - $($eventargs.data)" } } if($errorjob -eq $null) { "error - error job null" | out-file $log -encoding ascii -append write-host "error - error job null" } if($outputjob -eq $null) { "error - output job null" | out-file $log -encoding ascii -append write-host "error - output job null" } $process.start() $process.beginerrorreadline() if($process.startinfo.redirectstandardoutput) { $process.beginoutputreadline() } $ret = $null if($timeout -eq 0) { $process.waitforexit() $ret = $true } else { if(-not($process.waitforexit($timeout))) { write-host "error - process not completed, after specified timeout: $($timeout)" $ret = $false } else { $ret = $true } } # cancel event registrations remove-event * -erroraction silentlycontinue unregister-event -sourceidentifier common.launchprocess.error unregister-event -sourceidentifier common.launchprocess.output stop-job $errorjob.id remove-job $errorjob.id stop-job $outputjob.id remove-job $outputjob.id $ret } $repo = <your repo> $process = create-process $process.startinfo.redirectstandardoutput = $true $process.startinfo.filename = "git.exe" $process.startinfo.arguments = "clone $($repo)" $process.startinfo.workingdirectory = $dir launch-process $process $global:log terminate-process $process the log file must in global scope because routine runs event processing not in script scope.
sample of log file:
out - cloning ''... error - checking out files: 22% (666/2971)
error - checking out files: 23% (684/2971)
error - checking out files: 24% (714/2971)
Comments
Post a Comment