根据错误结果,在文件中循环(使用Powershell)并一次移动一个文件时出现问题
我使用Powershell针对多个XSD验证多个XML文件;这部分代码按预期工作,但是我还需要将任何无法验证的XML移到“无效”文件夹中。我试图使用ForEach循环遍历这些文件,然后使用If语句移动出错的任何文件。我的问题是所有文件都在移动,不管它们是否出错 我已经用我能想到的许多不同的方式写了这个循环,但是我没有得到我期望的结果。(我也在网上搜索了好几天才找到答案。)我需要ForEach将代码应用于每个文件,一次一个。这是我的语法问题吗?也许我遗漏了一些非常明显的东西,但我现在不知所措 我使用这个函数(在堆栈溢出上找到,如图所示,稍微调整)来验证XML根据错误结果,在文件中循环(使用Powershell)并一次移动一个文件时出现问题,powershell,if-statement,foreach,Powershell,If Statement,Foreach,我使用Powershell针对多个XSD验证多个XML文件;这部分代码按预期工作,但是我还需要将任何无法验证的XML移到“无效”文件夹中。我试图使用ForEach循环遍历这些文件,然后使用If语句移动出错的任何文件。我的问题是所有文件都在移动,不管它们是否出错 我已经用我能想到的许多不同的方式写了这个循环,但是我没有得到我期望的结果。(我也在网上搜索了好几天才找到答案。)我需要ForEach将代码应用于每个文件,一次一个。这是我的语法问题吗?也许我遗漏了一些非常明显的东西,但我现在不知所措 我使
function Test-XmlFile
{
<#
.Synopsis
Validates an xml file against an xml schema file.
.Example
PS> dir *.xml | Test-XmlFile schema.xsd
#>
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline=$true, Mandatory=$true)]
[SupportsWildcards()]
$SchemaFile,
[Parameter(ValueFromPipeline=$true, Mandatory=$true)]
[SupportsWildcards()]
[alias('Fullname')]
$XmlFile,
[scriptblock] $ValidationEventHandler = { Write-Error $args[1].Exception }
)
begin {
$schemaReader = New-Object System.Xml.XmlTextReader $SchemaFile
$schema = [System.Xml.Schema.XmlSchema]::Read($schemaReader, $ValidationEventHandler)
}
process {
$ret = $true
try {
$xml = New-Object System.Xml.XmlDocument
$xml.Schemas.Add($schema) | Out-Null
$xml.Load($XmlFile)
$xml.Validate({
throw ([PsCustomObject] @{
SchemaFile = $SchemaFile
XmlFile = $XmlFile
Exception = $args[1].Exception
})
})
} catch {
Write-Error $_
$ret = $false
}
$ret
}
end {
$schemaReader.Close()
}
}
对于$xml2和$xsd2变量,上面的ForEach循环也会重复。
(正如您从Out文件中看到的,我还在一个文本文件中捕获异常消息,用于排序日志。)
由于“If($error)”语句和我试图一次一个循环遍历文件的事实,我只希望移动那些出错并遇到异常的XML;但是,所发生的情况是,任何包含唯一字符串的XML(该字符串将其标识为$xml1或$xml2组的一部分)都会被移动到无效文件夹中,错误或无错误。那么,我错过了什么显而易见的痛苦呢??(顺便说一句,异常文本按预期填充错误日志,因此至少该部分工作正常。)
编辑:再想一想,我不应该说异常文本“按预期”填充日志。它确实会填充日志,但对于变量中包含的每个文件(例如$xml1中的每个文件),它会将消息写入日志文件一次,而不管该文件是否实际出错。因此,如果$xml1中有两个文件,但只有一个无效,则该无效文件的单个异常消息将写入日志两次。因此,它为循环的每个文件编写一些内容,而不管是否存在有效性或错误。希望这是有意义的。
$Error
为您提供了上一次错误的列表,如果上一次操作成功,它不会被清除-它仍将包含上一次遇到的错误。因此,在出现第一个错误后,每个文件都将被复制到“无效”目录
您的cmdlet中已经有错误处理,因此您可以修改该代码以同时处理将文件移动到其他目录的操作 您的
testxmlfile
函数输出一个布尔值,指示验证的成功状态,因此我建议直接使用它来确定验证成功与失败;此外,要捕获验证失败的细节,请使用-ErrorVariable
公共参数:
ForEach ($xml in $xml1) {
if (-not (Test-XmlFile -XmlFile $xml -SchemaFile $xsd1 -ErrorVariable err)) {
$err.Exception, "`n" | Out-File -Append "..\Schema Validation\Results\log.txt"
Move-Item $xml -Destination "..\Schema Validation\Invalid"
}
}
请注意,我已将-Append
添加到您的输出文件
调用中,以确保多个故障不会在日志文件中相互覆盖。此外,最好使用“`n”
或“`r`n”
或适当地使用[Environment]::NewLine]
创建换行符(换行符)
至于你所尝试的: 自动
$Error
变量是当前会话中发生的所有错误的集合(类型为[System.Collections.ArrayList]
)
因此,如果使用if($Error)
,实际上是询问到目前为止会话中是否至少发生了一个错误,而不是最近执行的命令是否报告了错误(最后一个错误将反映在$Error[0]
),因为一旦集合至少有一个条目,布尔上下文中的$Error
就会计算为$true
还有自动的$?
变量,它是一个布尔值,指示最近执行的命令是否报告了错误(至少一个错误)-不幸的是,这种行为存在不一致性(任何表达式都会将$?
重置为$true
,并且写入错误
不会将$?
设置为$false
,这与报告错误的已编译cmdlet不同),因此在您的情况下,最好依赖测试XmlFile
的显式布尔返回值。“上次错误”有点让人困惑;automatic$Error
变量是当前会话中发生的所有错误的集合,按逆时间顺序排列。
ForEach ($xml in $xml1) {
$xml | Test-XmlFile $xsd1
If ($Error) {
$Error[0].Exception, "`r" | Out-File "..\Schema Validation\Results\log.txt"
Move-Item $xml -Destination "..\Schema Validation\Invalid"
}}
ForEach ($xml in $xml1) {
if (-not (Test-XmlFile -XmlFile $xml -SchemaFile $xsd1 -ErrorVariable err)) {
$err.Exception, "`n" | Out-File -Append "..\Schema Validation\Results\log.txt"
Move-Item $xml -Destination "..\Schema Validation\Invalid"
}
}