【问题标题】:Convert a returned string in PowerShell to delimited data and export将 PowerShell 中返回的字符串转换为分隔数据并导出
【发布时间】:2015-11-11 21:13:02
【问题描述】:

我正在使用 PowerShell 远程配置来自不同供应商的存储阵列。该脚本允许我毫无问题地连接到阵列。从那里我使用 REST API 调用或通过 plink.exe 远程 SSH 运行命令。这是我的问题。在使用 plink 时,我需要查询数组,然后根据输出有条件地执行操作。问题是输出以字符串格式返回。这给我带来了问题,因为我想对返回的字符串的部分进行排序和提取,并根据输出向用户提供选项。

示例 - 列出卷

if ($sel_vendor -eq 3){ $ibm_ex_vols = & $rem_ssh $rem_ssh_arg1 $rem_ssh_arg2 $array_user"@"$array_mgmt_ip "-pw" $readpass "lsvdisk" foreach ($i in $ibm_ex_vols){ write-host $i } }

这是代码的输出

id name  IO_group_id IO_group_name status mdisk_grp_id mdisk_grp_name  capacity type    FC_id FC_name RC_id RC_name vdisk_UID                        fc_map_count copy_count fast_write_state se_copy_count RC_change compressed_copy_count parent_mdisk_grp_id parent_mdisk_grp_name
0  Test1 0           io_grp0       online 0            SVC_SYSTEM_POOL 10.00GB  striped                             600507680C80004E980000000000074B 0            1          empty            1             no        0                     0                   SVC_SYSTEM_POOL
1  Test2 0           io_grp0       online 0            SVC_SYSTEM_POOL 10.00GB  striped                             600507680C80004E980000000000074C 0            1          empty            1             no        0                     0                   SVC_SYSTEM_POOL
2  Test3 0           io_grp0       online 0            SVC_SYSTEM_POOL 10.00GB  striped                             600507680C80004E980000000000074D 0            1          empty            1             no        0                     0                   SVC_SYSTEM_POOL
3  Test4 0           io_grp0       online 0            SVC_SYSTEM_POOL 10.00GB  striped                             600507680C80004E980000000000074E 0            1          empty            1             no        0                     0                   SVC_SYSTEM_POOL

我希望能够存储此信息,然后从 id 和 name 列中选择标题和数据。我能够使用 out-file 命令将数据输出到 txt 文件。一旦我这样做了,我就使用 Excel 使用带分隔符的固定选项将其转换为分隔文件。虽然这可行,但我需要找出一个动态解决方案。

【问题讨论】:

  • 由于列的宽度会根据各种值的长度而变化(例如,具有长名称的系统使名称列比平时长),我要做的是执行正则表达式匹配捕获每个标题列表并包含空格,然后使用标题的长度来构造一个宽度分隔的正则表达式匹配,该匹配可以将每一行分解为一个带有值的 PSCustomObject,您将被设置为导出为 CSV 或做任何您想做的事情.

标签: powershell


【解决方案1】:

这是一个简单的解析函数,它可以拆分您的数据并生成具有要使用的属性的自定义对象:

function Parse-Data{
    begin{
        $Headers=$null
    }
    process{
        if(!$Headers){
            $Headers=
            [Regex]::Matches($_,'\S+')|
            ForEach-Object {
                $Header=$null
            } {
                if($Header){
                    $Header.SubArgs+=$_.Index-1-$Header.SubArgs[0]
                    $Header
                }
                $Header=[PSCustomObject]@{
                    Name=$_.Value
                    SubArgs=,$_.Index
                }
            } {
                $Header
            }
        }else{
            $String=$_
            $Headers|
            ForEach-Object {
                $Object=[ordered]@{}
            } {
                $Object.Add($_.Name,$String.Substring.Invoke($_.SubArgs).TrimEnd())
            } {
                [PSCustomObject]$Object
            }
        }
    }
}

你可以这样调用它:

$ibm_ex_vols=@'
id name  IO_group_id IO_group_name status mdisk_grp_id mdisk_grp_name  capacity type    FC_id FC_name RC_id RC_name vdisk_UID                        fc_map_count copy_count fast_write_state se_copy_count RC_change compressed_copy_count parent_mdisk_grp_id parent_mdisk_grp_name
0  Test1 0           io_grp0       online 0            SVC_SYSTEM_POOL 10.00GB  striped                             600507680C80004E980000000000074B 0            1          empty            1             no        0                     0                   SVC_SYSTEM_POOL
1  Test2 0           io_grp0       online 0            SVC_SYSTEM_POOL 10.00GB  striped                             600507680C80004E980000000000074C 0            1          empty            1             no        0                     0                   SVC_SYSTEM_POOL
2  Test3 0           io_grp0       online 0            SVC_SYSTEM_POOL 10.00GB  striped                             600507680C80004E980000000000074D 0            1          empty            1             no        0                     0                   SVC_SYSTEM_POOL
3  Test4 0           io_grp0       online 0            SVC_SYSTEM_POOL 10.00GB  striped                             600507680C80004E980000000000074E 0            1          empty            1             no        0                     0                   SVC_SYSTEM_POOL
'@-split'\r?\n'

$ibm_ex_vols|Parse-Data

【讨论】:

  • 您将数据包装在@'和'@中。然而,在我的代码中,这不是一个静态的信息块。这是存储在 $ibm_ex_vols 中的字符串。我可以简单地将文本替换为:@'$ibm_ex_vols'@
  • @SteveKehrer 你可以在这里使用变量。我进行了编辑。
  • 这很好用,但我希望只有 id 和 name 列的缩写输出。
  • @SteveKehrer 您可以简单地添加一些Format-* cmdlet 以不显示不需要的数据:$ibm_ex_vols|Parse-Data|Format-Table id,name
  • 太棒了!这完美地工作并且应该允许我对从数组返回的任何数据使用解析函数。感谢所有的建议。我真的很期待获得更多有关 PowerShell 的高级知识。
【解决方案2】:

这是一个更简单的基于数学的解决方案(假设 $ibm_ex_vols 包含作为字符串集合的输出):

$sOutFile = "outfile.csv"

# Splitting the headers line into chars.
$cChars = $ibm_ex_vols[0] -split ''

$cInsertIndices = @()
$j = 0
for ($i = 1; $i -lt $cChars.Count; $i++) {
    # If previous character is a whitespace and the current character isn't
    if ( ($cChars[$i - 1] -eq ' ') -and ($cChars[$i] -ne  ' ') ) {
        # we'll insert a delimiter here
        $cInsertIndices += $i + $j - 1
        $j++ # and each insert will increase the line length.
    }
}

foreach ($sLine in $ibm_ex_vols) {
    foreach ($i in $cInsertIndices) {
        # Adding delimiter.
        $sLine = $sLine.Insert($i, ',')
    }

    # Optionally we can also trim trailing whitespaces:
    # $sLine = $sLine -replace '\s+(?=,)'
    $sLine | Out-File -FilePath $sOutFile -Append
}

当然,这里我们不进行任何实际解析,因此无法使用方便的 PSObject。


最后,如果我们可以确定所有数据字段都将被填充并且不包含任何空白字符,我们就不需要依赖字段宽度并且可以进一步简化我们的代码,如下所示:

$sOutFile = "outfile.csv"

foreach ($sLine in $ibm_ex_vols) {
    $sLine = $sLine -replace '\s+', ','
    $sLine | Out-File -FilePath $sOutFile -Append
}

【讨论】:

  • 这里有一些基于你的想法的oneliner:$ibm_ex_vols-replace"(?<=^($(([Regex]::Matches($ibm_ex_vols[0],' \S').Index|%{".{$_}"})-join'|'))) ",','
  • 所以这是获得逗号分隔输出的一种不错的快捷方式。结果是它将输出打印到屏幕上,这不是我想要的。我真正需要的是能够获取该字符串并解析出 ID 和 Name 标题和值,并理想地显示在表格中。如果我可以将数据导出到 CSV、导入然后使用 select-object,我可以轻松地做到这一点。我希望尽可能避开进出口路线。
  • @PetSerAl 不错。虽然看起来像一个 perl hack。 ;) 我认为保留一些可读性更实用,即使是以一些额外的行为代价。
  • @SteveKehrer 您可以通过管道将字符串传递给ConvertFrom-Csv,并且无需导出和导入即可获取对象。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-06
  • 2020-05-04
  • 2019-08-23
  • 2011-02-02
  • 1970-01-01
  • 1970-01-01
  • 2012-10-27
相关资源
最近更新 更多