【问题标题】:How to convert powershell array to table如何将powershell数组转换为表
【发布时间】:2019-09-05 18:51:25
【问题描述】:

我在下面的链接中找到了关于该问题的类似帖子。

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 分钟及以上。

【问题讨论】:

  • 是你输入还是你需要的输出?您提供的链接还回答了您有关过滤的问题。你有什么问题?
  • 请向我们展示您的阵列真正的样子。您正在向我们展示一些输出,但从中我们不能说原始数组是对象数组,还是只是字段之间带有空格和/或制表符的文本行。
  • @vrdse 我已经编辑并显示了我想要的输出。
  • @Theo 我进行了编辑以获得更好的视图。我的问题与我共享的链接相同,唯一的区别是我无法从中创建表格。还有很多其他的文件我没有展示。
  • 确实看起来好多了,但是.. 这似乎不是一个数组,只是数组或表的一些文本输出。我们需要知道是什么分隔了这些字段,特别是因为有些字段是空的。在您的示例中,空格都是空格字符,所以对我来说这看起来像一个固定宽度的文本文件。是这样吗?

标签: powershell


【解决方案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


更新

现在我们终于知道数据来自制表符分隔的 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'

【讨论】:

  • 虽然我的输出宽度不同,但我会尝试一下。也许它会解决我面临的问题。一旦我尝试了,我会更新你的输出。
【解决方案2】:

呃。我很惊讶没有规范的方法来做到这一点。基于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

【讨论】:

  • 我会试一试并用输出更新你。
【解决方案3】:

我认为您在这里有几个要求。我将描述一种使用通用“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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-10
    • 1970-01-01
    • 2016-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-07
    • 2017-11-26
    相关资源
    最近更新 更多