8

GitHub からいくつかのリポジトリをダウンロードするスクリプトを書いています。リポジトリをダウンロードするコマンドは次のとおりです。

git clone "$RepositoryUrl" "$localRepoDirectory"

このコマンドを実行すると、表示したいコンソール ウィンドウに進行状況の情報が表示されます。

問題は、ダウンロード中にエラーが発生したかどうかも検出できるようにしたいことです。さまざまなストリームのリダイレクトについて説明しているこの投稿を見つけたので、試しました:

(git clone "$RepositoryUrl" "$localRepoDirectory") 2> $errorLogFilePath

これにより、stderr からファイルにエラーがパイプされますが、コンソールに進行状況の情報が表示されなくなります。

次のように Tee-Object を使用できます。

(git clone "$RepositoryUrl" "$localRepoDirectory") | Tee-Object -FilePath $errorLogFilePath

そして、私はまだ素晴らしい進行状況の出力を取得しますが、これは stderr ではなく stdout をファイルにパイプします。エラーの検出のみに関心があります。

ファイルまたは (できれば) 変数で発生したエラーを保存しながら、進行状況の情報をコンソール ウィンドウにパイプする方法はありますか? この投稿で説明されているように、さまざまなストリームを他のストリームにリダイレクトすることに答えがあるのではないかと感じていますが、よくわかりません。

========更新=======

git.exe が一般的な実行可能ファイルと異なるかどうかはわかりませんが、さらにテストを行ったところ、次のような結果が得られました。

$output = (git clone "$RepositoryUrl" "$localRepoDirectory")

$output には、コマンドが正常に完了したかエラーが発生したかに関係なく、常に「'[localRepoDirectory]' へのクローン作成中...」というテキストが含まれます。また、これを行うと、進行状況の情報が引き続きコンソールに書き込まれます。これは、進行状況情報が stdout 経由ではなく、他のストリームによって書き込まれていると思いますか?

エラーが発生した場合、エラーはコンソールに書き込まれますが、エラーの場合は典型的な赤、警告の場合は黄色ではなく、通常の白い前景色で表示されます。これがコマンドレット関数内から呼び出され、コマンドがエラーで失敗した場合、関数の -ErrorVariable (または -WarningVariable) パラメーターを介してエラーは返されません (ただし、独自の Write-Error を実行すると、-ErrorVariable を介して返されます) )。これにより、git.exe は stderr に書き込まないと思いますが、そうすると:

(git clone "$RepositoryUrl" "$localRepoDirectory") 2> $errorLogFilePath

エラーメッセージはファイルに書き込まれるので、stderrに書き込むと思います。だから今、私は混乱しています...

========更新2 =======

そのため、Byron の助けを借りて、新しいプロセスを使用してさらにいくつかの解決策を試しましたが、まだ必要なものを得ることができません。新しいプロセスを使用すると、コンソールに進捗状況が書き込まれません。

私が試した 3 つの新しい方法は、どちらも次のコードを共通して使用しています。

$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"

方法 1 - 新しいプロセスで実行し、その後出力を読み取ります。

$process.Start()
$process.WaitForExit()
Write-Host Output - $process.StandardOutput.ReadToEnd()    
Write-Host Errors - $process.StandardError.ReadToEnd()

方法 2 - 出力を同期的に取得する:

$process.Start()
while (!$process.HasExited)
{
    Write-Host Output - $process.StandardOutput.ReadToEnd()
    Write-Host Error Output - $process.StandardError.ReadToEnd()

    Start-Sleep -Seconds 1
}

これは、プロセスの実行中に出力を書き込むように見えますが、プロセスが終了するまで何も書き込みません。

方法 3 - 出力を非同期的に取得する:

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
}

これは、プロセスが動作している間にデータを出力しますが、それでも良い進行状況情報は表示されません:(

4

3 に答える 3

2

私はあなたの答えを持っていると思います。私はしばらく PowerShell を使用しており、いくつかのビルド システムを作成しました。スクリプトが少し長い場合は申し訳ありませんが、機能します。

$dir = <your dir>
$global:log = <your log file which must be in the 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 - The error job is null" | Out-File $log -Encoding ASCII -Append
        Write-Host "ERROR - The error job is null"
    }

    if($outputjob -eq $null) {
        "ERROR - The output job is null" | Out-File $log -Encoding ASCII -Append
        Write-Host "ERROR - The output job is 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 - The process is not completed, after the specified timeout: $($timeout)"
            $ret = $false
        }
        else
        {
            $ret = $true
        }
    }

    # Cancel the 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

イベント処理を実行するルーチンはスクリプト スコープ内にないため、ログ ファイルはグローバル スコープ内にある必要があります。

私のログファイルのサンプル:

アウト - '' に複製中... エラー - ファイルのチェックアウト: 22% (666/2971)
エラー - ファイルのチェックアウト: 23% (684/2971)
エラー - ファイルのチェックアウト: 24% (714/2971)

于 2013-07-16T10:15:13.713 に答える
1

これを行うには、git clone コマンドを高度な関数内に配置します。たとえば、次のようにします。

function Clone-Git {
    [CmdletBinding()]
    param($repoUrl, $localRepoDir)

    git clone $repoUrl $localRepoDir
}

Clone-Git $RepositoryUrl $localRepoDirectory -ev cloneErrors

$cloneErrors
于 2013-05-09T23:35:32.583 に答える
0

を使用System.Diagnostics.Processして Git を起動すると、すべてのエラーと出力をリダイレクトできます。

Inkscape でこの問題を解決する必要がありました。

$si = New-Object System.Diagnostics.ProcessStartInfo
$si.Arguments = YOUR PROCESS ARGS
$si.UseShellExecute = $false
$si.RedirectStandardOutput = $true
$si.RedirectStandardError = $true
$si.WorkingDirectory = $workingDir
$si.FileName = EXECUTABLE LOCATION
$process = [Diagnostics.Process]::Start($si)
while (!($process.HasExited))
{
    // Do what you want with strerr and stdout
    Start-Sleep -s 1  // Sleep for 1 second
}

もちろん、これを適切な引数を持つ関数でラップすることもできます...

于 2013-06-13T06:15:07.500 に答える