我使用PowerShell脚本来控制编译器的不同编译步骤(ghdl.exe)。
编译器有3种不同的输出格式:
由于处理stderr和stdout接缝非常错误,我使用了StackOverflow帖子中提供的方法:PowerShell: Manage errors with Invoke-Expression
以下是我添加消息着色的实现:
function Format-NativeCommandStreams
{ param([Parameter(ValueFromPipeline=$true)]$InputObject)
begin
{ $ErrorRecordFound = $false }
process
{ if (-not $InputObject)
{ Write-Host "Empty" }
elseif ($InputObject -is [System.Management.Automation.ErrorRecord])
{ $ErrorRecordFound = $true
$text = $InputObject.ToString()
Write-Host $text -ForegroundColor Gray
$stdErr = $InputObject.TargetObject
if ($stdErr)
{ #Write-Host ("err: type=" + $stdErr.GetType() + " " + $stdErr)
if ($stdErr.Contains("warning"))
{ Write-Host "WARNING: " -NoNewline -ForegroundColor Yellow }
else
{ Write-Host "ERROR: " -NoNewline -ForegroundColor Red }
Write-Host $stdErr
}
}
else
{ $stdOut = $InputObject
if ($stdOut.Contains("warning"))
{ Write-Host "WARNING: " -NoNewline -ForegroundColor Yellow }
else
{ Write-Host "ERROR: " -NoNewline -ForegroundColor Red }
Write-Host $stdOut
}
}
end
{ $ErrorRecordFound }
}
用法:
$Options = @(.....)
$Expr = "ghdl.exe -a " + ($Options -join " ") + " " + $File + " 2>&1"
$ret = Invoke-Expression $Expr | Format-NativeCommandStreams
通常,编译器每行发出一条消息(错误或警告)。如下面的屏幕截图所示,一些消息最多被切断为8行。这就是为什么我的输出着色不能按预期工作的原因。更多的行被检测为错误(误报),因此我无法在日志中找到真正的错误。
示例:
C:\Altera\15.0\quartus\eda\sim_lib\altera_mf.vhd:
39963:
53
:
warning:
universal integer bound must be numeric literal or attribute
C:\Altera\15.0\quartus\eda\sim_lib\altera_mf.vhd
:41794:36:warning: universal integer bound must be numeric literal or attribute
预期结果:
C:\Altera\15.0\quartus\eda\sim_lib\altera_mf.vhd:39963:53:warning: universal integer bound must be numeric literal or attribute
C:\Altera\15.0\quartus\eda\sim_lib\altera_mf.vhd:41794:36:warning: universal integer bound must be numeric literal or attribute
据我所见,编译器(ghdl.exe)确实以完整行的形式发出消息。
问题:
答案 0 :(得分:2)
可执行文件stderr
上的完整输出只是分散在System.Management.Automation.ErrorRecord
类型的多个对象上。实际拆分似乎是非确定性的(*)。此外,部分字符串存储在属性Exception
内,而不是TargetObject
。只有第一个ErrorRecord
具有非空TargetObject
。也就是说,为什么包含字符串"warning"
的输出的后续行未格式化为黄色和白色,如下所示:
:41794:36:warning: universal integer bound must be numeric literal or attribute
您的灰色输出来自每个toString()
的{{1}}方法,该方法返回此记录的属性ErrorRecord
的值。
因此必须将所有消息连接在一起,以便在格式化之前获得整个输出。这些消息中保留了换行符。
编辑 :( *)它取决于程序的写入/刷新调用与Powershell的读取调用的顺序。如果在我的测试程序中的每个Exception.Message
之后添加fflush(stderr)
,则会有更多fprintf()
个对象。除了第一个似乎是确定性的,它们中的一些包括2个输出线,其中一些包含3个。
我没有使用GHDL,而是使用新的Visual Studio项目开始,并使用以下代码创建了一个控制台应用程序(HelloWorldEx)。它只是在ErrorRecord
stderr
然后我编译了程序并在Powershell中执行它: (编辑:从我自己的脚本中删除调试代码)
#include "stdafx.h"
#include <stdio.h>
int _tmain(int argc, _TCHAR* argv[])
{
// Print some warning messages on stderr
for(int i=0; i<70; i++) {
fprintf(stderr, "warning:%070d\n", i); // 80 bytes per line including CR+LF
}
return 0; // exit code is not relevant
}
这是脚本的输出。正如您所看到的,我的程序输出分为3 .\HelloWorldEx.exe 2>&1 | set-variable Output
$i = 0
$Output | % {
Write-Host ("--- " + $i + ": " + $_.GetType() + " ------------------------")
Write-Host ($_ | Format-List -Force | Out-String)
$i++
}
(实际可能不同):
ErrorRecords
答案 1 :(得分:1)
你可以进行一些调试来解决这个问题。我建议从这样的事情开始:
ghdl.exe <whatever args you supply> 2>&1 | set-variable ghdlOutput
$i = 0
$ghdlOutput | % {write-host "$i `t: " $_.gettype() "`t" $_ ; $i++}
这将列出行号,输出行的类型以及输出的每个实时。您可能需要调整一些代码以使输出看起来不错。
从那里你可以看到编译器是否真的将错误分成多行。如果是,你可以尝试设计一个策略来确定哪些行是stdout,哪些是stderr。如果没有,那么你将有一些线索来调试上面的脚本。
或者可以打包整个方法并使用.NET system.diagnostics.process类并将stdout和stderr重定向为单独的流。使用带有ProcessStartInfo的Start方法。如果需要,您应该能够谷歌这样做的例子。
答案 2 :(得分:0)
似乎我以马丁·扎贝尔(Martin Zabel)为例解决了这个问题,结果证明该解决方案很平淡无奇。
事实是很长一段时间以来,我都无法从来电中得到字符`r`n。事实证明这很简单。
唯一需要做的就是用`n代替`r!
该解决方案将在控制台的任何宽度上正常工作,因为反向功能已被删除。
此外,这可能是解决将处理后的数据实时返回到控制台的问题的基础。唯一需要做的就是捕获传入的单个r或n以获取新的“可变字符串”,并根据任务将处理后的数据使用r或n发送回控制台。 >
cls
function GetAnsVal {
param([Parameter(Mandatory=$true, ValueFromPipeline=$true)][System.Object[]][AllowEmptyString()]$Output,
[Parameter(Mandatory=$false, ValueFromPipeline=$true)][System.String]$firstEncNew="UTF-8",
[Parameter(Mandatory=$false, ValueFromPipeline=$true)][System.String]$secondEncNew="CP866"
)
function ConvertTo-Encoding ([string]$From, [string]$To){#"UTF-8" "CP866" "ASCII" "windows-1251"
Begin{
$encFrom = [System.Text.Encoding]::GetEncoding($from)
$encTo = [System.Text.Encoding]::GetEncoding($to)
}
Process{
$Text=($_).ToString()
$bytes = $encTo.GetBytes($Text)
$bytes = [System.Text.Encoding]::Convert($encFrom, $encTo, $bytes)
$encTo.GetString($bytes)
}
}
$all = New-Object System.Collections.Generic.List[System.Object];
$exception = New-Object System.Collections.Generic.List[System.Object];
$stderr = New-Object System.Collections.Generic.List[System.Object];
$stdout = New-Object System.Collections.Generic.List[System.Object]
$i = 0;$Output | % {
if ($_ -ne $null){
if ($_.GetType().FullName -ne 'System.Management.Automation.ErrorRecord'){
if ($_.Exception.message -ne $null){$Temp=$_.Exception.message | ConvertTo-Encoding $firstEncNew $secondEncNew;$all.Add($Temp);$exception.Add($Temp)}
elseif ($_ -ne $null){$Temp=$_ | ConvertTo-Encoding $firstEncNew $secondEncNew;$all.Add($Temp);$stdout.Add($Temp)}
} else {
#if (MyNonTerminatingError.Exception is AccessDeniedException)
$Temp=$_.Exception.message | ConvertTo-Encoding $firstEncNew $secondEncNew;
$all.Add($Temp);$stderr.Add($Temp)
}
}
$i++
}
[hashtable]$return = @{}
$return.Meta0=$all;$return.Meta1=$exception;$return.Meta2=$stderr;$return.Meta3=$stdout;
return $return
}
Add-Type -AssemblyName System.Windows.Forms;
& C:\Windows\System32\curl.exe 'api.ipify.org/?format=plain' 2>&1 | set-variable Output;
$r = & GetAnsVal $Output
$Meta0=""
foreach ($el in $r.Meta0){
$Meta0+=$el
}
$Meta0=($Meta0 -split "[`r`n]") -join "`n"
$Meta0=($Meta0 -split "[`n]{2,}") -join "`n"
[Console]::Write($Meta0);
[Console]::Write("`n");