如何将Powershell数组转换为表

时间:2019-08-25 04:56:30

标签: powershell

我在下面的链接中找到了与此问题类似的帖子。

How to fetch first column from given powershell array?

由于某些字段丢失并且无法执行操作,因此我无法将其直接转换为表。

Customer ID        Client Name        Computer Name        Computer Brand        Duration        Connection Time        Lang
123                first last         127.0.0.1            lenovo                10:00           8/18/2019 6:00 PM      Eng
1                  lastname           127.0.0.2            apple                 2:30:00         8/18/2019 1:00 AM      Chn  
86                 user3              127.0.0.1            dell                                  8/18/2019 2:00 PM 
21                 user4              127.0.0.4            apple                 30:00           8/17/2019 1:00 PM      Eng

我想首先与连接了30分钟以上的特定用户进行过滤,然后列出其ID。

更新

结果应该是

1
21

因为它们已连接30分钟以上。

3 个答案:

答案 0 :(得分:1)

如果显示的数据确实是固定宽度文件的输出,则需要尝试获取每个字段的宽度以进行解析。这里的一个障碍是原始标头名称包含一个空格字符,我们需要用下划线替换它。

为此,您可以使用以下功能:

function ConvertFrom-FixedWith {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true, Position = 0)]
        [string[]]$Content
    )

    $splitter   = '§¤¶'             # some unlikely string: Alt-21, [char]164, Alt-20  
    $needQuotes = '^\s+|[",]|\s+$'  # quote the fields if needed

    function _FWClean ([string]$field) {
        # internal helper function to clean a field value with regards to quoted fields
        $field = $_.Trim() -replace '(?<!\\)\\"|""', '§DQUOTE¶'
        if ($field -match '^"(.*)"$')  { $field = $matches[1] }
        if ($field -match $needQuotes) { $field = '"{0}"' -f $field }
        return $field -replace '§DQUOTE¶', '""'
    }

    # try and calculate the field widths using the first header line
    # this only works if none of the header names have spaces in them
    # and where the headers are separated by at least one space character.

    Write-Verbose "Calculating column widths using first row"
    $row = ($Content[0] -replace '\s+', ' ').Trim()
    $fields = @($row -split ' ' ) # | ForEach-Object { _FWClean $_ })
    $ColumnBreaks = for ($i = 1; $i -lt $fields.Length; $i++) {
        $Content[0].IndexOf($fields[$i]) 
    }
    $ColumnBreaks = $ColumnBreaks | Sort-Object -Descending

    Write-Verbose "Splitting fields and generating output"
    $Content | ForEach-Object {
        if ($null -ne $_ -and $_ -match '\S') {
            $line = $_
            # make sure lines that are too short get padded on the right
            if ($line.Length -le $ColumnBreaks[0]) { $line = $line.PadRight(($ColumnBreaks[0] + 1), ' ') }
            # add the splitter string on every column break point
            $ColumnBreaks | ForEach-Object { 
                $line = $line.Insert($_, $splitter)
            }
            # split on the splitter string, trim, and dedupe possible quotes
            # then join using the delimiter character
            @($line -split $splitter | ForEach-Object { _FWClean $_ }) -join ','
        }
    } | ConvertFrom-Csv    # the result is an array of PSCustomObjects
}

有了该功能,就可以像这样解析文本:

$text = @"
Customer_ID        Client_Name        Computer_Name        Computer_Brand        Duration        Connection_Time        Lang
123                first last         127.0.0.1            lenovo                10:00           8/18/2019 6:00 PM      Eng
1                  lastname           127.0.0.2            apple                 2:30:00         8/18/2019 1:00 AM      Chn  
86                 user3              127.0.0.1            dell                                  8/18/2019 2:00 PM 
21                 user4              127.0.0.4            apple                 30:00           8/17/2019 1:00 PM      Eng
"@ -split '\r?\n'

# replace the single space characters in the header names by underscore
$text[0] = $text[0] -replace '(\w+) (\w+)', '$1_$2'

# the 'ConvertFrom-FixedWith' function takes a string array as input
$table = ConvertFrom-FixedWith -Content $text

#output on screen
$table | Format-Table -AutoSize

# export to CSV file
$table | Export-Csv -Path 'D:\test.csv' -NoTypeInformation

输出(在屏幕上)

Customer ID Client Name Computer Name Computer Brand Duration Connection Time   Lang
----------- ----------- ------------- -------------- -------- ---------------   ----
123         first last  127.0.0.1     lenovo         10:00    8/18/2019 6:00 PM Eng 
1           lastname    127.0.0.2     apple          2:30:00  8/18/2019 1:00 AM Chn 
86          user3       127.0.0.1     dell                    8/18/2019 2:00 PM     
21          user4       127.0.0.4     apple          30:00    8/17/2019 1:00 PM Eng 

如果您输入的$ text已经是一个字符串数组,该字符串数组存储了我们在问题中看到的所有ines,则省略-split '\r?\n'


将输入解析到PsCustomObjects表之后,您可以借助另一个小助手功能来获得连接30分钟或更长时间的客户:

function Get-DurationInMinutes ([string]$Duration) {
    $h, $m, $s = (('0:{0}' -f $Duration) -split ':' | Select-Object -Last 3)
    return [int]$h * 60 + [int]$m
}

($table | Where-Object { (Get-DurationInMinutes $_.Duration) -ge 30 }).Customer_ID

这将输出

1
21


更新


现在,我们终于知道数据来自TAB分隔的CSV文件,您就不需要ConvertFrom-FixedWith函数了。

仅使用数据来自文件来导入数据

$table = Import-Csv -Path 'D:\customers.csv' -Delimiter "`t"

,如果它来自另一个命令的输出,则为字符串或字符串数​​组:

$table = $original_output | ConvertFrom-Csv -Delimiter "`t"

然后,像上面一样使用Get-DurationInMinutes帮助函数来获取已连接30分钟以上的客户ID:

function Get-DurationInMinutes ([string]$Duration) {
    $h, $m, $s = (('0:{0}' -f $Duration) -split ':' | Select-Object -Last 3)
    return [int]$h * 60 + [int]$m
}

($table | Where-Object { (Get-DurationInMinutes $_.Duration) -ge 30 }).'Customer ID'

答案 1 :(得分:0)

嗯。我很惊讶没有一种规范的方法可以做到这一点。基于https://www.reddit.com/r/PowerShell/comments/211ewa/how_to_convert_fixedwidth_to_pipedelimited_or/

# 0                  19                 38                   59                    81              97                     120 123
# Customer ID        Client Name        Computer Name        Computer Brand        Duration        Connection Time        Lang
# 123                first last         127.0.0.1            lenovo                10:00           8/18/2019 6:00 PM      Eng
# 1                  lastname           127.0.0.2            apple                 2:30:00         8/18/2019 1:00 AM      Chn
# 86                 user3              127.0.0.1            dell                                  8/18/2019 2:00 PM
# 21                 user4              127.0.0.4            apple                 30:00           8/17/2019 1:00 PM      Eng


$cols = 0,19,38,59,81,97,120,123 # fake extra column at the end, assumes all rows are that wide

$firstline = get-content columns.txt | select -first 1
$headers = for ($i = 0; $i -lt $cols.count - 1; $i++) {
  $firstline.substring($cols[$i], $cols[$i+1]-$cols[$i]).trim()
}

# string Substring(int startIndex, int length)

$lines = Get-Content columns.txt | select -skip 1 
$lines | ForEach {
  $hash = [ordered]@{}
  for ($i = 0; $i -lt $headers.length; $i++) {
    $hash += @{$headers[$i] = $_.substring($cols[$i], $cols[$i+1]-$cols[$i]).trim()}
  }
  [pscustomobject]$hash
} 

输出:

PS /Users/js/foo> ./columns | ft

Customer ID Client Name Computer Name Computer Brand Duration Connection Time   Lan
----------- ----------- ------------- -------------- -------- ---------------   ---
123         first last  127.0.0.1     lenovo         10:00    8/18/2019 6:00 PM Eng
1           lastname    127.0.0.2     apple          2:30:00  8/18/2019 1:00 AM Chn
86          user3       127.0.0.1     dell                    8/18/2019 2:00 PM 
21          user4       127.0.0.4     apple          30:00    8/17/2019 1:00 PM Eng

答案 2 :(得分:-1)

我认为您在这里有两个要求。我将描述一种使用通用的“ for循环”和正则表达式的方法,您可以根据自己的需要进行调整。有更好的方法(Powershell快捷方式),但是根据您要求的方式,我将假设理解是您的目标,因此,如果您具有任何编程语言的背景,那么此代码应该很好用。希望这会有所帮助!

# Here is your data formatted in an array.  Missing values are just empty fields.
# You could have fewer or more fields, but I've broken up your data into nine fields
# (0-8 when counting elements in an array)

# Customer ID, FName, LName, ComputerHostName, Brand, Duration, ConnectionDate, ConnectionTime, Lang
$myarray = @(
    ('123',  'firstname',    'lastname', '127.0.0.1', 'lenovo',  '10:00',    '8/18/2019', '6:00 PM', 'Eng'),
    ('1',    'lastnam',      '',         '127.0.0.2', 'apple',   '2:30:00',  '8/18/2019', '1:00 AM', 'Chn'),
    ('86',   'user3',        '',         '127.0.0.1', 'dell',    '04:33',    '8/18/2019', '2:00 PM', ''),
    ('21',   'user4',        '',         '127.0.0.4', 'apple',   '30:00',    '8/17/2019', '1:00 PM', 'Eng')
)

# This is a generic for loop that prints the ComputerHostName, which is the 4th column.
# The 4th column is column #3 if counting from zero (0,1,2,3)
# I'm using a regular expression to match duration above 30 minutes with the '-match' operator
for ( $i = 0; $i -lt $myarray.Length; $i++ ) {
    if ( $myarray[$i][5] -match "[3-5][0-9]:[0-9][0-9]$" ){

        "$($myarray[$i][5]) - $($myarray[$i][3])"
    }
}

打印结果:

2:30:00 - 127.0.0.2
30:00 - 127.0.0.4