从Powershell中的文本文件构造数组

时间:2018-05-15 16:14:16

标签: powershell

我有一个烦人的报告输出(诅咒是惠普),这不是我可以通过查询形成的东西 - 它或多或少都是或多或少。我想从每个" chunk"中取两行。输出并从中构造一个数组。我认为这将是一个简单的split()操作,但没有这样的运气。输出的样本如下:

Medium identifier : 1800010a:54bceddd:1d8c:0007

Medium label             : [ARJ170L6] ARJ170L6
Location                 : [TapeLibrary:    24]
Medium Owner             : wfukut01
Status                   : Poor
Blocks used  [KB]        : 2827596544
Blocks total [KB]        : 2827596544
Usable space [KB]        : 1024
Number of writes         : 16
Number of overwrites     : 4
Number of errors         : 0
Medium initialized       : 19 January 2015, 11:43:32
Last write               : 26 April 2016, 21:02:12
Last access              : 26 April 2016, 21:02:12
Last overwrite           : 24 April 2016, 04:48:55
Protected                : Permanent
Write-protected          : No


Medium identifier : 1800010a:550aa81e:3a0c:0006

Medium label             : [ARJ214L6] ARJ214L6
Location                 : External
Medium Owner             : wfukut01
Status                   : Poor
Blocks used  [KB]        : 2904963584
Blocks total [KB]        : 2904963584
Usable space [KB]        : 0
Number of writes         : 9
Number of overwrites     : 7
Number of errors         : 0
Medium initialized       : 19 March 2015, 10:42:45
Last write               : 30 April 2016, 22:14:19
Last access              : 30 April 2016, 22:14:19
Last overwrite           : 29 April 2016, 13:41:35
Protected                : Permanent
Write-protected          : No

如果这项工作的最终输出会创建一个与此类似的数组,那么理想的是:

Location                     UsableSpace
---------                    -------------
External                     0
TapeLibrary                  1024

所以我可以(例如)查询输出,以便我可以对数组中的数据进行操作:

$myvar | where-object { $_.Location -eq "TapeLibrary" }

也许有更好的方法?我很乐意听到他们的声音!

5 个答案:

答案 0 :(得分:2)

最简单的方法是使用命令本身和select某些属性。如果该命令是powershell cmdlet,则应返回对象

$output = Some-HPCommand | select 'Medium label', 'Location' 

然后您可以访问特定属性:

$output.'Medium label'
$output.Location

如果您可以提供完全命令,我可以更准确地写出来。

人们学习PowerShell时最大的问题是他们将输出视为 String ...... PowerShell中的所有内容都是面向对象的,一旦你开始考虑 Objects < / em>,处理数据变得更加容易;换句话说,总是尝试将输出作为对象或对象数组来处理。它会让你的生活变得更加容易。

答案 1 :(得分:2)

如果该命令不是像Kolob Canyon的答案那样的Powershell cmdlet,那么您需要解析文本。这是使用-match和正则表达式找到LocationUsable space [KB]的行并在冒号后面找到单词字符的不典型示例。

((Get-Content C:\Example.txt -Raw) -split 'Medium identifier') | ForEach-Object {
    [void]($_ -match 'Location\s+:\s(.*?(\w+).*)\r')
    $Location = @($Matches.values | Where-Object {$_ -notmatch '\W'})[0]
    [void]($_ -match 'Usable\sspace\s\[KB\]\s+:\s(.*?(\w+).*)\r')
    $UsableSpace = @($Matches.values | Where-Object {$_ -notmatch '\W'})[0]
    if ($Location -or $UsableSpace){
        [PSCustomObject]@{
            Location = $Location
            UsableSpace = $UsableSpace
        }
    }
}

由于这是非常脆弱和不优雅的,所以尽可能与对象进行交互要好得多。

答案 2 :(得分:2)

假设数据与它看起来一样规则,您可以使用多个赋值从数组中提取数据,如下所示:

$data = 1, 2, "ignore me", 3, 10, 22, "ignore", 30
$first, $second, $null, $third, $data = $data

第一个,第二个和第四个数组元素进入变量,“忽略我”在$null中被丢弃,剩下的数据返回到数据中。在您的情况下,这看起来像:

# Read the file into an array
$data = Get-Content data.txt

# Utility to fix up the data row
function FixUp ($s)
{
    ($s -split ' : ')[1].Trim()
}

# Loop until all of the data is processed
while ($data)
{
    # Extract the current record using multiple assignment
    # $null is used to eat the blank lines
    $identifier,$null,$label,$location,$owner,$status,
        $used,$total,$space,$writes,$overwrites,
        $errors, $initialized, $lastwrite, $lastaccess,
        $lastOverwrite, $protected, $writeprotected,
        $null, $null, $data = $data

    # Convert it into a custom object 
    [PSCustomObject] [ordered] @{
        Identifier   = fixup $identifier
        Label        = fixup $label
        location     = fixup $location
        Owner        = fixup $owner
        Status       = fixup $status
        Used         = fixup $used
        Total        = fixup $total
        Space        = fixup $space
        Write        = fixup $writes
        OverWrites   = fixup $overwrites
        Errors       = fixup $errors
        Initialized  = fixup $initialized
        LastWrite    = [datetime] (fixup $lastwrite)
        LastAccess   = [datetime] (fixup $lastaccess)
        LastOverWrite = [datetime] (fixup $lastOverwrite)
        Protected    = fixup $protected
        WriteProtected = fixup $writeprotected
    }
}

提取数据后,您可以按照自己想要的方式对其进行格式化

答案 3 :(得分:2)

这看起来非常规律,所以我说有三种典型方法。

首先:你自己的bucket-fill-trigger-empty解析器,加载线直到你到达下一个触发器(&#34; Medium标识符&#34;),然后将桶清空到管道并开始一个新的

类似的东西:

$bucket = @{}

foreach ($line in Get-Content -LiteralPath C:\path\data.txt)
{
    # if full, empty bucket to pipeline
    if ($line -match '^Medium identifier')
    {
        [PSCustomObject]$bucket
        $bucket = @{}
    }

    # add line to bucket (unless it's blank)
    if (-not [string]::IsNullOrWhiteSpace($line))
    {
        $left, $right = $line.Split(':', 2)
        $bucket[$left.Trim()] = $right.Trim()
    }
}

# empty last item to pipeline
[PSCustomObject]$bucket

根据品味识别数字,日期等进行调整

第二:多线正则表达式:我试过,但不能。它看起来像是:

# Not working, but for example:
$r = @'
Medium identifier    : (?<MediumIdentifier>.*)
\s*
Write-protected      : (?<WriteProtected>.*)
Blocks used  [KB]    : (?<BlockesUsed>.*)
Medium label         : (?<MediumLabel>.*)
Last write           : (?<LastWrite>.*)
Medium Owner         : (?<MediumOwner>.*)
Usable space [KB]    : (?<UsableSpaceKB>.*)
Number of overwrites : (?<NumberOfOverwrites>.*)
Last overwrite       : (?<LastOverwrite>.*)
Medium identifier    : (?<MediumIdentifier>.*)
Blocks total [KB]    : (?<BlocksTotalKB>.*)
Number of errors     : (?<NumberOfErrors>.*)
Medium initialized   : (?<MediumInitialized>.*)
Status               : (?<Status>.*)
Location             : (?<Location>.*)
Protected            : (?<Protected>.*)
Number of writes     : (?<NumberOfWrites>.*)
Last access          : (?<LastAccess>.*)
\s*
'@

[regex]::Matches((get-content C:\work\a.txt -Raw), $r, 
    [System.Text.RegularExpressions.RegexOptions]::IgnoreCase + 
    [System.Text.RegularExpressions.RegexOptions]::Singleline
    )

第三:ConvertFrom-String - http://www.lazywinadmin.com/2014/09/powershell-convertfrom-string-and.htmlhttps://blogs.technet.microsoft.com/ashleymcglone/2016/09/14/use-the-new-powershell-cmdlet-convertfrom-string-to-parse-klist-kerberos-ticket-output/然后制作模板后

Get-Content data.txt | ConvertFrom-String -TemplateFile .\template.txt

答案 4 :(得分:1)

如果每个部分的格式相同,即可用空间部分总是从该位置向下5行,那么您可以将Select-String与context参数结合使用。像这样:

Select-String .\your_file.txt -Pattern '(?<=Location\s*:\s).*' -Context 0, 5 | % {
    New-Object psobject -Property @{
        Location = (($_.Matches[0] -replace '\[|\]', '') -split ':')[0]
        UsableSpace = ($_.Context.PostContext[4] -replace '^\D+(\d+)$', '$1' )
    }
}