通过通用函数更新不同的数组

时间:2018-08-11 15:07:49

标签: arrays function powershell

我目前正在制作Powershell脚本,该脚本将分析来自邮件服务器的多个日志文件,以收集各种统计信息,这些统计信息将存储在许多不同的数组中。

我有以下代码片段作为更新其中一个数组的示例。

#Update arrays
#Overall array
$objNewValue = New-Object -TypeName PSObject
$objNewValue = $PSOSSLOverall | Where-Object {($_.Version -contains $strVersion -and $_.Cipher -contains $strCipher -and $_.Bits -contains $strBits)}
If ($objNewValue -ne $null) {
  try {
    Write-Verbose "$strVersion $strCipher $strBits is already in the array, so we'll update TimeSeen value"
    $objNewValue.TimesSeen++
    $objNewValue.LastSeen = $dtTimestamp
  } #try
  catch {
    Write-Host "Something whent wrong while attempting to update an existing object in the overall array" -BackgroundColor DarkRed
    Write-Host "Current line: $strtemp[$i]"
    Write-Host "Current values: $dtTimestamp <-> $strVersion <-> $strCipher <-> $strBits"
    Write-Host "Current array:"
    $PSOSSLOverall | Sort-Object -Property Version, Cipher -Descending | Format-Table -AutoSize
    Write-Host "Exception object:"
    $_
  } #catch
} #If Check for existence in Overall array
Else {
  try {
    Write-Verbose "$strVersion $strCipher $strBits is not in the array, so it will be added "
    $objNewValue = New-Object -TypeName PSObject
    Add-Member -InputObject $objNewValue -MemberType 'NoteProperty' -Name 'Version' -Value $strVersion
    Add-Member -InputObject $objNewValue -MemberType 'NoteProperty' -Name 'Cipher' -Value $strCipher
    Add-Member -InputObject $objNewValue -MemberType 'NoteProperty' -Name 'Bits' -Value $strBits
    Add-Member -InputObject $objNewValue -MemberType 'NoteProperty' -Name 'TimesSeen' -Value 1
    Add-Member -InputObject $objNewValue -MemberType 'NoteProperty' -Name 'Percentage' -Value 0
    Add-Member -InputObject $objNewValue -MemberType 'NoteProperty' -Name 'FirstSeen' -Value $dtTimestamp
    Add-Member -InputObject $objNewValue -MemberType 'NoteProperty' -Name 'LastSeen' -Value $dtTimestamp
    $PSOSSLOverall += $objNewValue
  } #try
  catch {
    Write-Host "Something whent wrong while attempting to add a new object to the overall array"
    Write-Host "Current line: $strtemp[$i]"
    Write-Host "Current values: $dtTimestamp <-> $strVersion <-> $strCipher <-> $strBits"
    Write-Host "Exception object:"
    $_
  } #catch
} #Else Check for existence in Overall array

但是,当我有多达10个或更多的数组需要更新时,结果将是很多类似的代码,因为每次更改的行都相对较少,例如要更新的数组,where子句,使用的变量和数组中的列数。

是否有可能创建一个可以处理更新不同数组的函数?

谢谢。

-更新-

解释上面的代码片段:在运行脚本的此部分之前,所有变量都已设置。实际上,$ strtemp [$ i]是所有数据的来源,就像日志文件中的当前行一样,我从中提取所需的数据并将其放在各个变量中。

首先,我搜索有问题的数组(在本例中为$ PSOSSLOverall),以查看当前行中的数据是否已在数组中。如果$ objNewValue不为null,则数据已经存在,然后我增加一个计数器并为该“行”数据更新日期戳。如果$ objNewValue为null,则该数据还不存在,然后我们将一个now对象(行)与来自各种变量的数据一起添加到数组中。

每次尝试都配有try / catch部分以进行错误处理。

最终结果将是一个看起来像这样的数组(百分比列在其他位置计算):

Content of array

其他数组具有不同数量的列,我想这很难使它们具有通用功能来更新它们。

1 个答案:

答案 0 :(得分:0)

我发现自己处于类似情况一两次。您必须重构代码,将不灵活的静态定义转换为参数变量。这样做的目的是将数据与程序逻辑分开,以便您可以执行一些操作,例如在不同情况下将一组不同的属性名称和值传递给同一函数。

我发现有几个地方可以提高灵活性:

  1. 参数化用于查找匹配记录的Where-Object表达式,因此您不必为列和值的每种组合编写相同的代码。请参阅下面的hasPropertyValues。它所做的只是对传递给它的每个名称/值对执行-and串联的-contains操作。

  2. 为每种情况参数化要更改的数据。您的代码在找到匹配的记录或找不到匹配的记录时会执行某些操作。将这些动作从脚本主体中拉出,并输入一个输入参数,当您完成加密数据集的处理后,该参数可以更改,并且必须移至另一个参数。 UpdateRecords函数采用哈希表参数来定义添加新记录和找到匹配记录时数据的形状。

有关示例,请参见下文。我认为您可以将此处的一些想法适应您的代码。

# this is a convenience function allowing us to test multiple name-value pairs against an object
function hasPropertyValues {
    Param(
        [object] $inputObject,
        [hashtable] $properties
    )
    $result = $true
    foreach($name in $properties.Keys){
        $result = $result -and ($inputObject.$name -contains $properties[$name])
    }
    Write-Output $result
}

# this function evaluates each object in $inputDataset
# if an object matches the name-values defined in $matchProperties
# it updates the records according to $updateRecordProperties
# if no records are found which match $matchProperties
# a new object is created with the properties in both $matchProperties
# and $newRecordProperties
# All results are written to the pipeline, including unmodified objects
function UpdateRecords{
    Param (
        [object[]] $inputDataset,
        [hashtable] $matchProperties,
        [hashtable] $updateRecordProperties, 
        [hashtable] $newRecordProperties
    )

    $numberOfMatchingRecords = 0
    foreach ($record in $inputDataset){
        if ( hasPropertyValues -inputObject $record -properties $matchProperties) {
            # a record with matching property values found. 
            # Update required attributes
            $numberOfMatchingRecords++
            foreach($name in $updateRecordProperties.Keys){
                if ($updateRecordProperties[$name] -is 'ScriptBlock'){
                    # if the update is a scriptblock, we invoke the scriptblock
                    # passing the record as input. The result of the invocation
                    # will be set as the new attribute value
                    $newValue = & $updateRecordProperties[$name] $record
                } else {
                    $newValue = $updateRecordProperties[$name]
                }
                $record | Add-Member -Force -MemberType NoteProperty -Name $name -Value $newValue
            }
        }
        Write-Output $record
    }
    if ($numberOfMatchingRecords -eq 0) {
        # no records found with the search parameters
        $newRecord = New-Object -TypeName psobject -Property $newRecordProperties
        foreach($key in $matchProperties.Keys){
            $newRecord | Add-Member -MemberType NoteProperty -Name $key -Value $matchProperties[$key] -Force
        }
        Write-Output $newRecord
    }
}

[object[]] $TestDataset= @(New-Object psobject -Property @{
        version='TLSv1.2'
        cipher='ECDHE-RSA-AES256-GCM-SHA384'
        Bits=256
        TimesSeen = 1833
        Percentage = 87578
        FirstSeen = [DateTime]::Now
        LastSeen = [DateTime]::Now
    })

function TestUpdateRecords{

    $encryptionNewRecordDefaults = @{
        TimesSeen = 1
        Percentage = 0
        FirstSeen = [DateTime]::Now
        LastSeen = [DateTime]::Now
    }

    $encryptionUpdateAttributes = @{
            LastSeen = [DateTime]::Now
            TimesSeen = {$ARGS[0].TimesSeen + 1}
    }

    # test adding a new record
    UpdateRecords -inputDataset $TestDataset `
        -matchProperties @{ Version='TLSv1.0';cipher='unbreakable';bits=720} `
        -updateRecordProperties $encryptionUpdateAttributes `
        -newRecordProperties  $encryptionNewRecordDefaults

    # test modifying a matching record
    UpdateRecords -inputDataset $things `
        -matchProperties @{Version='TLSv1.2';cipher='ECDHE-RSA-AES256-GCM-SHA384';bits=256} `
        -updateRecordProperties $encryptionUpdateAttributes `
        -newRecordProperties $encryptionNewRecordDefaults
}

TestUpdateRecords

有很多不同的方法来实现这种重构。例如,您可以将特定于数据集的逻辑提取到脚本块中,并将其传递给主循环函数。

另一种可能性是深入研究PowerShell的面向对象功能,并尝试围绕每个数据集构建类。这样可以以一种愉快的方式封装“更新”和“新”操作。我还没有足够的Powershell OO功能知识来尝试。