Вопрос или проблема
У меня есть следующая функция PowerShell. Я исключил части функции, которые не имеют отношения к делу.
Function Add-TradeDetailElementBlocks
{
[CmdletBinding()]
Param
(
[Parameter(ValueFromPipeline=$False, Mandatory=$True)]
[ValidateNotNullOrEmpty()]
[string]$Path,
[Parameter(ValueFromPipeline=$False, Mandatory=$True)]
[string]$DateFormat,
[Parameter(ValueFromPipeline=$False, Mandatory=$True)]
[string]$TradeImportXPath,
[Parameter(ValueFromPipeline=$False, Mandatory=$True)]
[ValidateNotNullOrEmpty()]
[System.Data.DataRow[]]$BlockList,
[Parameter(ValueFromPipeline=$False, Mandatory=$True)]
[int]$DealId
)
$Xml = New-Object -TypeName System.Xml.XmlDocument
$File = Get-childItem -Path $Path
$Xml.Load($File)
$TradeImportNode = $Xml.SelectSingleNode($TradeImportXPath)
$PriceMultiplier,$AmountMultiplier = Get-Multiplier $DealId
foreach($Block in $BlockList)
{
$TradeDetailsElement = $Xml.CreateElement('tradeDetail')
$TradeImportNode.AppendChild($TradeDetailsElement) | Out-Null
$DatesElement = $Xml.CreateElement('dates')
$TradeDetailsElement.AppendChild($DatesElement) | Out-Null
$TradeDetailsElement.dates = Convert-DateToString -Date $Block.dates -Format $DateFormat
$Block.price = ([double]$Block.price * $PriceMultiplier).ToString("N10")
$Block.amount = ([double]$Block.amount * $AmountMultiplier).ToString("N6")
if($Block.times -ne $Null)
{
$TimesElement = $Xml.CreateElement('times')
$TradeDetailsElement.AppendChild($TimesElement) | Out-Null
$TradeDetailsElement.times = [string]($Block.times)
}
:
:
$Xml.Save($Path)
}
}
Эта функция читает значения из объекта строки данных (т.е. $Block) и записывает значения в XML-файл. Структура XML-файла в данном случае не имеет значения.
Если я сохраню эту функцию в файле PowerShell и импортирую файл в свой скрипт, используя точечную нотацию, я получаю ошибку на строке if($Block.times -ne $Null)
, потому что свойство times
не существует в строке данных. Это можно ожидать.
Однако, если функция содержится в модуле, который импортируется в скрипт с помощью Import-Module
, тот же код будет работать без проблем (даже если элемент times
не существует в строке данных).
Что вызывает это различие в поведении?
Ответ или решение
Вопрос о том, почему функция PowerShell ведет себя по-разному в зависимости от того, импортируется ли она из файла или из модуля, можно объяснить несколькими ключевыми аспектами, связанными с областью видимости и контекстом выполнения.
Когда вы импортируете функцию из файла с помощью точечной нотации (. С:\путь\к\вашему\скрипту.ps1
), она выполняется в контексте вызывающего скрипта, что означает, что все переменные и объекты, доступные в этом контексте, подвержены влиянию локальных настроек и данных. В вашем случае, если свойство times
не существует в объекте DataRow
, то условие if($Block.times -ne $Null)
приводят к ошибке, поскольку PowerShell пытается получить доступ к этому отсутствующему свойству, что и вызывает сбой.
С другой стороны, когда функция содержится в модуле и импортируется с помощью Import-Module
, она выполняется в своей собственной области видимости. Модули имеют своего рода изолированное окружение, в котором PowerShell интерпретирует отсутствие свойства times
как несущественное. Это происходит благодаря тому, что PowerShell использует определенные механизмы обработки ошибок, позволяя обходить такие ситуации при работе с объектами. Поэтому, когда функция ищет свойство times
, а его нет, PowerShell не выбрасывает исключение, а просто возвращает $null
, что позволяет вашему коду продолжить выполняться.
Основные моменты, объясняющие различия в поведении:
-
Область видимости (Scope):
- Функции, загружаемые из файла, работают в контексте вызывающего скрипта.
- Функции из модуля выполняются в своей изолированной области.
-
Обработка отсутствующих свойств:
- В контексте скрипта в случае отсутствия свойства PowerShell вызывает ошибку.
- В модуле отсутствующее свойство игнорируется, что позволяет избегать сбоев.
-
Безопасность и устойчивость к ошибкам:
- Модули более устойчивы к ошибкам, и их код, как правило, более защищен от вызовов отсутствующих свойств или методов.
Для устранения различий и обеспечения надежности кода независимо от способа его импорта, вы можете внести изменения в вашу функцию, добавив предварительную проверку на наличие свойства times
перед его использованием:
if ($Block.PSObject.Properties['times'] -and $Block.times -ne $Null) {
$TimesElement = $Xml.CreateElement('times')
$TradeDetailsElement.AppendChild($TimesElement) | Out-Null
$TradeDetailsElement.times = [string]($Block.times)
}
Такой подход поможет избежать ошибочных ситуаций как в контексте файла, так и в модуле, что сделает ваш код более универсальным и надежным.