首页 > 解决方案 > 根据错误结果循环遍历文件(使用 Powershell)并一次移动一个文件的问题

问题描述

我正在使用 Powershell 针对多个 XSD 验证多个 XML 文件;这部分代码按预期工作,但是我还需要将任何无法验证的 XML 移动到“无效”文件夹。我正在尝试使用 ForEach 遍历这些文件,然后 - 使用 If 语句 - 移动任何出错的文件。我的问题是所有文件都被移动,无论它们是否出错。

我已经用尽可能多的不同方式编写了这个循环,但我没有得到我期望的结果。(我也在网上搜索了几天以找到答案。)我需要 ForEach 将代码应用于每个文件,一次一个。这是我的语法问题吗?也许我错过了一些非常明显的东西,但我现在不知所措。

我正在使用这个函数(在 Stack Overflow 上找到,如图所示,稍作调整)来验证 XML。

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()
    }
}

以下是我选择 XML 以针对其给定模式进行验证的方式。

$allfiles = "..\Schema Validation\XMLs\*.xml"

$xml1 = Get-ChildItem $allfiles -Recurse | Select-String "<UniqueElement>" -List | Resolve-Path
$xml2 = Get-ChildItem $allfiles -Recurse | Select-String "<UniqueElement>" -List | Resolve-Path

$xsd1 = "..\Schema Validation\Schemas\Schema1.xsd"
$xsd2 = "..\Schema Validation\Schemas\Schema2.xsd"

这是对我不起作用的 ForEach 循环。(在其当前配置中,尽管我已经编写了十几种不同的方式。)

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 循环也对 $xml2 和 $xsd2 变量重复。(正如您从 Out-File 中看到的那样,我还在一个文本文件中捕获异常消息以用于各种日志。)

由于“If ($Error)”语句以及我试图一次遍历文件的事实,我只希望移动那些出错并遇到异常的 XML;但是,发生的情况是,任何包含将其标识为 $xml1 或 $xml2 组的一部分的唯一字符串的 XML 都将移动到 Invalid 文件夹,错误或无错误。那么我错过了什么令人痛苦的显而易见的事情?(顺便说一句,异常文本按预期填充错误日志,因此至少该部分按我希望的那样工作。)

编辑:再想一想,我不应该说异常文本“按预期”填充了日志。它确实填充了日志,但是对于变量中包含的每个文件(例如,$xml1 中的每个文件),它都会将消息写入日志文件一次,无论文件是否实际出错。因此,如果 $xml1 中有两个文件,但只有一个无效,则该无效文件的单个异常消息将被写入日志两次。所以它为每个循环的文件写一些东西,不管有效性或错误。希望这是有道理的。

标签: powershellif-statementforeach

解决方案


$Error为您提供最后一个错误的列表,如果上一个操作成功,它不会被清除 - 它仍然包含最后遇到的错误。因此,在第一个错误之后,每个文件都将被复制到“无效”目录。

您已经在 cmdlet 中进行了错误处理,因此您可以修改该代码以同时处理将文件移动到其他目录的问题。


推荐阅读