Анализатор XML в Powershell и перемещение файла после проверки

Следующий скрипт уже работает довольно хорошо. Я могу сверить данный файл xml с файлом xsd и зарегистрировать все ошибки проверки. Моя проблема в том, что я хочу переместить файл xml, если не возникает ошибка проверки. Но я не знаю, где разместить утверждение move-item. Я всегда получаю ошибку file can not moved because it is still in use. Вот код. Надеюсь, кто-то может помочь.

# Get all XML files in the folder that start with "products"
$xmlFiles = Get-ChildItem -Path $sourceDir -Filter products*.xml -File

foreach ($xmlFile in $xmlFiles) {
    $path = $xmlFile.FullName #$sourceDir + $xmlFile

    # Checking if file exits
    if (-not (Test-Path $path)) { 
        Write-Warning "The XML file $path does not exist."
        logger -ErrMessage "File not exits." -log_file $logfile -xml_file $path

    # Checking if file is readable
    } elseif ($xmlFile.IsReadOnly) {
        Write-Warning "XML file not readable: $path"
        logger -ErrMessage "File not readable." -log_file $logfile -xml_file $path
    
    } else {
        # Set up validation settings
        $valSettings = [System.Xml.XmlReaderSettings]::new();
        $valSettings.ValidationType = [System.Xml.ValidationType]::Schema;
        $valSettings.CheckCharacters = $true;
        $valSettings.ValidationFlags = ([System.Xml.Schema.XmlSchemaValidationFlags]::ProcessIdentityConstraints -bor [System.Xml.Schema.XmlSchemaValidationFlags]::ReportValidationWarnings)
        $valSettings.Schemas.Add($URL, $xsdFilename);

        # Store validation errors in an array
        $errors = New-Object System.Collections.Generic.List[System.Xml.Schema.ValidationEventArgs]

        # Create a validation event handler
        $validationHandler = {
            param ([object] $sender, [System.Xml.Schema.ValidationEventArgs] $e)

            $lineNumber = ""
            $linePosition = ""

            # Get the line number and position of the error
          #  if ($e.Exception) {
                $lineNumber = $e.Exception.LineNumber
                $linePosition = $e.Exception.LinePosition
          #  }

            if ($e.Severity -eq [System.Xml.Schema.XmlSeverityType]::Warning) {
                Write-Host("WARNING___: ")
                $errors.Add($e)
                Write-Warning($e.Message, $lineNumber)
                Write-Host("--Line: $lineNumber, Position: $linePosition")
                logger -ErrMessage $e.Message -line $lineNumber -log_file $logfile -xml_file $path
            } 
            elseif ($e.Severity -eq [System.Xml.Schema.XmlSeverityType]::Error) {
                if ($e.Exception -and $e.Exception.Message.StartsWith("The 'text' element is not declared.")) {
                    $e.Severity = [System.Xml.Schema.XmlSeverityType]::Warning
                    Write-Host("WARNING___: ")
                    $errors.Add($e)
                    Write-Warning($e.Exception.Message)
                    Write-Host("--Line: $lineNumber, Position: $linePosition")
                    logger -ErrMessage $e.Message -line $lineNumber -log_file $logfile -xml_file $path
                }
                else {
                    $errors.Add($e)
                    Write-Warning $e.Message;
                    Write-Host("--Line: $lineNumber, Position: $linePosition");
                    if (![string]::IsNullOrEmpty($logfile)) {
                        #write-host $e.Exception.GetType().FullName
                        logger -ErrMessage $e.Message -line $lineNumber -log_file $logfile -xml_file $path
                    } 
                }
            } 
        }

        # Set up validation event handler to capture errors
        #$valSettings.ValidationEventHandler += {$errors.Add($_)}#/ turn it on, if 'Add_ValidationEventHandler' runs into error

        $destination = "C:\Users\BeKa-Coca\Downloads\XML_Validation\$xmlFile"

        try {
            # Read each xml node, validate it and close the xml file
        $valSettings.Add_ValidationEventHandler($validationHandler)   
        $xmlReader = [System.Xml.XmlReader]::Create([System.IO.File]::OpenRead($path), $valSettings);
            while ($xmlReader.Read()) { }
            $xmlReader.Close()
            #Copy-Item $xmlFile.FullName $destination
            #Move-Item $xmlFile.FullName $destination -Force

        } catch{
            $lineNumber = ""
            $linePosition = ""
         #   if ($_.Exception) {
                $lineNumber = [regex]::Match($_.Exception.Message, "Zeile (\d+)").Groups[1].Value
                $linePosition = [regex]::Match($_.Exception.Message, "Position (\d+)").Groups[1].Value
         #   }
            $errorDesc = $_.Exception.Message  
            write-warning $_.Exception.Message
            Write-Host("--Line: $lineNumber, Position: $linePosition")
            logger -ErrMessage $errorDesc -line $lineNumber -log_file $logfile -xml_file $path
        }
        finally {

            # make sure the xml reader is closed even if an exception occurs
           if ($xmlReader) {
                $xmlReader.Dispose()

                # Copy / move only valid xml files
                If (-not($errors)) {
                    #Copy-Item $xmlFile.FullName $destination -Force
                    Move-Item $xmlFile.FullName $destination -Force
                } else {WRITE-Host "ERROR!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"}
            }
        }
    } #End If-else

             #Copy-Item $xmlFile.FullName $destination
             #logger -ErrMessage "The file is in use by another process." -log_file $logfile -xml_file $path

 } #End ForEach Loop

Созданный xmlReader закрывается, прежде чем я попытаюсь переместить файл. Но файл по-прежнему блокируется, и я не знаю, почему.

На первый взгляд: [1] Если вы добавите -File в строку Get-ChildItem, вы обязательно получите существующие объекты FileInfo (нет необходимости проверять, существует ли он) [2] Из-за [1] внутри вашей переменной цикла $xmlFile есть объект со многими свойствами, одним из которых является .FullName, поэтому используйте его для переменной $path и используйте свойство $xmlFile.IsReadOnly вместо того, чтобы снова получать элемент. [3] $xmlReader.Dispose() также закрывается, так что вы можете удалить $xmlReader.close() в блоке finally. [4] переместите строку Move-Item .. из блока finally и поместите ее последней строкой в ​​блок try{}.

Theo 05.05.2023 20:44

[5] Вместо тестирования if ($xmlReader -ne $null) просто сделайте if ($xmlReader)

Theo 05.05.2023 20:45

Я изменил свой код, но перемещение файла по-прежнему невозможно. Я получаю сообщение об ошибке xml file is locked by another process. куда поставить $xmlFile.IsReadOnly? Это ключ, чтобы заставить его работать правильно, не сталкиваясь с ошибкой?

the_floor 05.05.2023 21:10

Возможно, вы изменили код, но не сообщение на SO, поэтому мы не можем сказать, что вы сделали. Замените if ((Get-Item $path).IsReadOnly) на if ($xmlFile.IsReadOnly)

Theo 05.05.2023 21:24

Что касается данных советов, я редактирую свой оригинальный пост. Но он все еще сталкивается с той же ошибкой: file could not be moved, because it is locked by another process. Почему xmlReader.close() не закрывает object правильно? Чтобы запустить скрипт, я использую следующую команду в консоли Windows . ./xml_validation_logging_v02.ps1. Может из-за этого ошибка?

the_floor 06.05.2023 08:37
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
5
81
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Хорошо, попробуйте эту новую переработку вашего кода.
Конечно, я не могу видеть, что именно делает ваша функция logger, но я не могу представить, что из-за этого файл остается заблокированным.

Я изменил код сейчас, чтобы инициализировать все переменные настроек проверки сверху И добавить это: $valSettings.CloseInput = $true, чтобы убедиться, что базовый поток или TextReader закрывается. Смотрите CloseInput

# Set up validation settings
$valSettings = [System.Xml.XmlReaderSettings]::new()
$valSettings.ValidationType  = [System.Xml.ValidationType]::Schema
$valSettings.CheckCharacters = $true
$valSettings.CloseInput      = $true  # close the underlying stream or TextReader when the reader is closed
$valSettings.ValidationFlags = ([System.Xml.Schema.XmlSchemaValidationFlags]::ProcessIdentityConstraints -bor 
                                [System.Xml.Schema.XmlSchemaValidationFlags]::ReportValidationWarnings)
$valSettings.Schemas.Add($URL, $xsdFilename)

# Store validation errors in an List object
$errors = New-Object System.Collections.Generic.List[System.Xml.Schema.ValidationEventArgs]

# find all xml files in the source directory
$xmlFiles = Get-ChildItem -Path $sourceDir -Filter 'products*.xml' -File

foreach ($xmlFile in $xmlFiles) {
    $errors.Clear()
    $path = $xmlFile.FullName
    if ($xmlFile.IsReadOnly) {
        Write-Warning "XML file not readable: $path"
        logger -ErrMessage "File not readable." -log_file $logfile -xml_file $path
        continue   # skip this file and proceed with the next
    } 

    # Create a validation event handler
    $validationHandler = {
        param ([object] $sender, [System.Xml.Schema.ValidationEventArgs] $e)
        # Get the line number and position of the error
        $lineNumber   = $e.Exception.LineNumber
        $linePosition = $e.Exception.LinePosition

        if ($e.Severity -eq [System.Xml.Schema.XmlSeverityType]::Warning) {
            Write-Host "WARNING___: "
            [void]$errors.Add($e)
            Write-Warning ($e.Message, $lineNumber)
            Write-Host "--Line: $lineNumber, Position: $linePosition"
            logger -ErrMessage $e.Message -line $lineNumber -log_file $logfile -xml_file $path
        } 
        elseif ($e.Severity -eq [System.Xml.Schema.XmlSeverityType]::Error) {
            if ($e.Exception.Message -like "The 'text' element is not declared*") {
                Write-Host "ERROR___: "
                [void]$errors.Add($e)
                Write-Warning $e.Exception.Message
                Write-Host "--Line: $lineNumber, Position: $linePosition"
                logger -ErrMessage $e.Message -line $lineNumber -log_file $logfile -xml_file $path
            }
            else {
                [void]$errors.Add($e)
                Write-Warning $e.Message
                Write-Host "--Line: $lineNumber, Position: $linePosition" 
                # write-host $e.Exception.GetType().FullName
                logger -ErrMessage $e.Message -line $lineNumber -log_file $logfile -xml_file $path
            }
        } 
    }

    $destination = 'C:\Users\BeKa-Coca\Downloads\XML_Validation'  # just the path here

    try {
        # Read each xml node, validate it and close the xml file
        $valSettings.Add_ValidationEventHandler($validationHandler)   
        $xmlReader = [System.Xml.XmlReader]::Create([System.IO.File]::OpenRead($path), $valSettings)
        while (!$xmlReader.EOF -and $xmlReader.Read()) {}
        $xmlReader.Dispose()
        $xmlReader = $null
        # Copy / move only valid xml files
        if ($errors.Count -eq 0) {
            #$xmlFile | Copy-Item -Destination $destination
            $xmlFile | Move-Item -Destination $destination -Force
        }
    } 
    catch{
        $lineNumber   = [regex]::Match($_.Exception.Message, "Zeile (\d+)").Groups[1].Value
        $linePosition = [regex]::Match($_.Exception.Message, "Position (\d+)").Groups[1].Value
        $errorDesc    = $_.Exception.Message
        Write-Warning $errorDesc
        Write-Host "--Line: $lineNumber, Position: $linePosition"
        logger -ErrMessage $errorDesc -line $lineNumber -log_file $logfile -xml_file $path
    }
    finally {
        # make sure the xml reader is closed even if an exception occurs
        if ($xmlReader) { $xmlReader.Dispose() }
        $xmlReader = $null
    }
} # End foreach loop

Спасибо за помощь. Но он все еще работает с той же ошибкой. Файл заблокирован и не может быть перемещен. + $xmlFile | Move-Item -Destination $destination -Force + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : WriteError: (C:\Users\BeKa-C..._230505_401.xml:FileInfo) [Move-Item], IOException + FullyQualifiedErrorId : MoveFileInfoItemIOError,Microsoft.PowerShell.Commands.MoveIt‌​emCommand Еще какие советы по решению проблемы? Это как-то связано с командой, которую я использую для запуска скрипта . .\xml_validation_logging_v03.ps1?

the_floor 06.05.2023 19:43

Спасибо. Оно работает. Правильные файлы перемещаются. Вот функция регистратора: function logger { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [string]$ErrMessage, [Parameter(Mandatory=$false)] [string]$line, [Parameter(Mandatory=$true)] [string]$log_file, [Parameter(Mandatory=$false)] [string]$xml_file ) $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" $logMsg = "[$timestamp] -- [Source: $xml_file] -- [Line: $line] -- [ERROR] $ErrMessage" Add-Content -Path $log_file -Value $logMsg }

the_floor 07.05.2023 17:40

Есть одна последняя проблема. Если $xmlFiles = Get-ChildItem -Path $sourceDir -Filter 'products*.xml' -File получить более одного XML-файла, одна ошибка проверки будет зарегистрирована дважды. Я проверяю только один файл, регистрация ошибок работает правильно и регистрирует только одну ошибку. Думаю, это как-то связано с петлей.

the_floor 07.05.2023 17:42

Я сделал еще пару тестов. Пока у меня есть только один XML-файл для проверки, все работает так, как ожидалось, и каждая ошибка получает один раз. Но если я получаю в папке два xml-файла, все ошибки для второго файла регистрируются дважды. Что может быть причиной этой проблемы?

the_floor 07.05.2023 23:12

Хотите проверить это еще раз? Я был бы признателен за ваши отзывы. Хорошего дня.

the_floor 08.05.2023 19:05

@the_floor Я внес небольшие изменения. Если вы по-прежнему получаете двойные сообщения в журнале, не могли бы вы опубликовать их полностью

Theo 08.05.2023 19:23

Большой! Потрясающе. Оно работает. Большое спасибо. Что касается обработки ошибок: есть ли что-то еще, что имеет смысл также регистрировать, чтобы улучшить обработку ошибок? Наслаждайтесь оставшейся частью дня.

the_floor 08.05.2023 19:48

@the_floor Ах, приятно слышать, что ваша функция ведения журнала выглядит нормально. Если вы считаете, что мой ответ решил вашу проблему, отметьте этот вопрос как «готово». Вы можете увидеть в туре как это сделать.

Theo 08.05.2023 21:03

Другие вопросы по теме