Проблема зацикливание файлов (используя Powershell) и перемещение их по одному, в зависимости от результата ошибки - PullRequest
2 голосов
/ 07 мая 2019

Я использую Powershell для проверки нескольких файлов XML на наличие нескольких XSD;эта часть кода работает, как и ожидалось, однако мне также нужно переместить любой XML, который не удается проверить, в папку «Invalid».Я пытаюсь перебрать эти файлы с помощью 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, я также записываю сообщение об исключении в текстовый файл для своего рода журнала.)

Я ожидал, что только те XML-файлы, в которых произошла ошибка и возникнет исключение, будутпереместился из-за оператора If ($ Error) и того факта, что я пытаюсь перебирать файлы по одному;однако происходит то, что любой XML, который содержит уникальную строку, которая идентифицирует его как часть группы $ xml1 или $ xml2, перемещается в папку Invalid, ошибка или нет ошибки.Так что же до боли очевидная вещь, которую я пропускаю ??(Между прочим, текст исключения заполняет журнал ошибок, как и ожидалось, поэтому, по крайней мере, эта часть работает, как я надеялся.)

РЕДАКТИРОВАТЬ: Если подумать, я не должен говорить, что текст исключения заполняет журнал "как и ожидалось".Он заполняет журнал, но записывает сообщение в файл журнала один раз для каждого файла, включенного в переменную (например, для каждого файла в $ xml1), независимо от того, был ли файл действительно ошибочным.Таким образом, если в $ xml1 есть два файла, но только один является недействительным, сообщение об одном исключении для этого одного недопустимого файла будет записано в журнал дважды.Так что он пишет что-то для каждого зацикленного файла, независимо от правильности или ошибок.Надеюсь, что это имеет смысл.

Ответы [ 2 ]

2 голосов
/ 07 мая 2019

$Error дает вам список последних ошибок, он не очищается, если предыдущая операция выполнена успешно - он все равно будет содержать последние обнаруженные ошибки.Таким образом, каждый файл будет скопирован в каталог «invalid» после первой ошибки.

В вашем командлете уже есть обработка ошибок, поэтому вы можете изменить этот код, чтобы он также обрабатывал перемещение файла в другой каталог.

0 голосов
/ 07 мая 2019

Ваша функция Test-XmlFile выводит логическое значение, указывающее статус успешности проверки, поэтому я предлагаю использовать это непосредственно для определения успешности проверки по сравнению с ошибкой; Кроме того, чтобы зафиксировать особенности ошибки проверки, используйте общий параметр -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 к вашему вызову Out-File, чтобы множественные сбои не перезаписывали друг друга в файле журнала. Кроме того, лучше использовать "`n" или "`r`n" или, соответственно для платформы, [Environment]::NewLine] для создания новой строки (перенос строки).


Что касается , что вы пытались :

Автоматическая переменная $Error представляет собой набор (типа [System.Collections.ArrayList]) всех ошибок, которые произошли в текущем сеансе , с обратной хронологической ошибкой.

Таким образом, если вы используете if ($Error), вы фактически спрашиваете, произошла ли хотя бы одна ошибка в сеансе , а не сообщала ли последняя выполненная команда об ошибках (последняя из которых будет отражено в $Error[0]), поскольку $Error в логическом контексте оценивается как $true, если в коллекции есть хотя бы одна запись.

Существует также автоматическая переменная $?, которая является логическим значением, указывающим, сообщала ли последняя выполненная команда об ошибке (хотя бы одна ошибка) - однако, к сожалению, такое поведение страдает от несоответствий (любое ) выражение сбрасывает $? в $true, а Write-Error делает не устанавливает $? в $false, в отличие от скомпилированных командлетов ошибок отчетов), поэтому лучше полагаться на явное логическое возвращаемое значение из Test-XmlFile в вашем случае.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...