【问题标题】:Powershell Loop through a text file and split by colonPowershell循环遍历文本文件并用冒号分割
【发布时间】:2020-06-09 16:29:12
【问题描述】:

我有一个内容类似于以下的文本文件:

----------------------------------------
Title: Textbook
Client: ABC
----------------------------------------
----------------------------------------
Title: Comic book
Client: DEF
----------------------------------------

我想用冒号分割每一行,并将左侧存储在一个名为“Title”的变量中,将右侧存储在另一个变量中,依此类推......这样当我打印输出时,我会得到类似于以下内容的内容:

$Title has been sold to $Client

到目前为止,这是我的脚本。我在Powershell reading multiple variables from text file 中尝试了一些示例,但运气不佳,我似乎无法弄清楚我当前的脚本有什么问题。

$data = Get-Content "C:\Users\user\Downloads\test.log" | Where { $_ -notmatch '^-.*' -and $_ -notmatch '^\s*$' } 

$outputFromLoop = @() 
$data | foreach-object {

    $key, $value = ($_ -split ':',2).trim()
    $outputFromLoop[$key] = $value

}
$outputFromLoop

【问题讨论】:

    标签: powershell loops foreach


    【解决方案1】:

    您可以将文件导入为 csv 并使用 : 作为分隔符

    $content = Import-Csv C:\temp\file.txt -Delimiter : -Header Field1, Field2 | ? { $_.Field1 -notmatch "-+" }
    
    $i = 0
    while ($i -lt $csv.Count) {
    
      $title = $csv[$i++].Field2
      $client = $csv[$i++].Field2
      Write-output "$title sold to $client"
    }
    
    #Prints to console:
    Textbook sold to ABC
    Comic book sold to DEF
    
    

    或者您可以读取文件并仅获取匹配的 Titles 和 Clients.. 然后对每个使用相同的索引以获得所需的输出。

    $content = Get-Content C:\temp\file.txt
    
    $Titles = $content -match "Title: " | % { $_ -replace "Title: ", "" }
    $Clients = $content -match "Client: " | % { $_ -replace "Client: ", ""}
    
    if ($Titles.Length -ne $Clients.Length) {
        Write-Output "Not the same"
    }
    
    $outputFromLoop = @{}
    for($i = 0; $i -lt $titles.Length; $i++) {
        Write-Output "$($Titles[$i]) sold to $($Clients[$i])"
        $outputFromLoop[$Titles[$i]] = $Clients[$i]
    }
    
    # Prints the same
    Textbook sold to ABC
    Comic book sold to DEF
    
    $outputFromLoop:
    Name                           Value                                                                                                                                                                                                                 
    ----                           -----                                                                                                                                                                                                                 
    Textbook                       ABC                                                                                                                                                                                                                   
    Comic book                     DEF
    

    当然,这在很大程度上依赖于您的文本文件将一个接一个地包含 Title 和 Client 的事实。

    【讨论】:

    • 即使我尝试了 $outputFromLoop ,它也对我不起作用......有没有办法可以循环遍历所有这些变量,因为我的文本文件中还有其他字段只有 $Titles 和 $Clients?例如以 $price_point 的价格卖给 ABC 的教科书...
    • @HoDiep 我修复了变量...由于某种原因,我在循环中得到了 $output 而不是 $OutputFromLoop。再试一次
    【解决方案2】:

    我会将文件读取为一个大的多行字符串,将其拆分为 ------ 行以获取单独的记录,进行一些格式更新(将冒号转换为等号并删除一些空格),然后使用 @ 987654322@ 制作您可以使用的对象。

    $RawText = Get-Content C:\Path\To\File.txt -raw
    $Sales = $RawText -split '(?:^|[\r\n])-+(?:[\r\n]|$)' -replace '(Title|Client):\s*','$1='|Where{$_}|ForEach-Object{New-Object PSObject -Property (ConvertFrom-StringData $_)}
    $Sales | ForEach-Object {
        "{0} was sold to {1}" -f $_.Title, $_.Client
    }
    

    结果:

    Textbook was sold to ABC
    Comic book was sold to DEF
    

    它还为您提供了一组对象,您可以使用这些对象来跟踪每个客户的销售额,或查看谁购买了哪些商品。 (如$Sales | Group Client)。

    【讨论】:

    • 谢谢。我试过了,虽然它有效,但我无法理解逻辑:(
    【解决方案3】:

    这里还有另一种方法。 [grin] 这个使用命名的捕获组和 dotnet 正则表达式引擎的 (?ms) 多行和单行选项来解析文本块。

    #region >>> fake reading in a text file as one multiline string
    #    in real life, use Get-Content -Raw
    $InStuff = @'
    ----------------------------------------
    Title: Textbook
    Client: ABC
    ----------------------------------------
    ----------------------------------------
    Title: Comic book
    Client: DEF
    ----------------------------------------
    '@
    #endregion >>> fake reading in a text file as one multiline string
    
    # split by the lines of hyphens
    $SplitInStuff = ($InStuff -split '-{1,}').
        # trim away the unwanted whitespace & non-printing chars
        Trim().
        # remove all the blank lines
        Where({$_})
    
    $Results = foreach ($IS_Item in $SplitInStuff)
        {
        $Null = $IS_Item -match '(?ms)Title: (?<Title>.+)$.*Client: (?<Client>.+)'
    
        # send the object out to the $Results collection
        [PSCustomObject]@{
            Title = $Matches.Title
            Client = $Matches.Client
            }
    
        Write-Host ('{0} has been sold to {1}.' -f $Matches.Title, $Matches.Client)
        Write-Host ('=' * 10)
        }
    
    # if you want only the screen display, then you can remove the "$Results = " and the next line
    #    the would allow you to use the extracted info - perhaps save it to a CSV file
    $Results
    

    输出...

    Textbook has been sold to ABC.
    ==========
    Comic book has been sold to DEF.
    ==========
    
    Title       Client
    -----       ------
    Textbook... ABC   
    Comic bo... DEF
    

    【讨论】:

    • 你知道是否有一种方法可以根据之前的捕获组动态分配捕获组名称吗?类似(这行不通)'(?ms)(Title|Client):\s*(?&lt;\1&gt;\S.*)' ...我只是想以防万一订单不一致,这不会太在意。
    • @TheMadTechnician - 我想不出任何方法可以一步涵盖这个想法。 [blush] 但是,可以使用两个匹配步骤...每个项目一个。
    • @Lee_Dailey 谢谢。我测试了你的解决方案,但我得到了多行重复的行,而不是每行一个......我的文本文件有 4 个文本块,同一个客户端有 2 个块......并且它重复了同一个客户端 6 次......
    • 如果您的示例数据与您的实际数据不匹配……您如何期望任何人提供适用于 -actual_ 数据的代码? [grin] 我建议您提供真实的示例数据来测试代码。
    【解决方案4】:

    这是使用switch 语句的方法:

    $hash = [ordered]@{}
    switch -regex -file test.log {
      '^-+' { if ($hash.Count -ne 0) { 
                "{0} has been sold to {1}" -f $hash.Title,$hash.Client
              }
              $hash = [ordered]@{} 
      }
      '^([^:]+):(.+)' { $key,$value = $matches[1].Trim(),$matches[2].Trim()
                        $hash.Add($key,$value) 
      }
    }
    

    说明:

    -regex 开关使用正则表达式匹配文件的每一行。 ^-+ 匹配以一个或多个 - 开头的任何行。 ^([^:]+):(.+) 匹配任何以非冒号字符开头、后跟冒号、后跟字符的行。第一组括号包括捕获组 1 ($matches[1])。第二组括号包含捕获组 2 ($matches[2])。

    $matches 将填充在包含: 且不以- 开头的行上。每次到达以- 开头的行时,包含ClientTitle 的哈希表($hash) 将检索其值并以请求格式的字符串输出。然后重新初始化哈希表。

    【讨论】:

    • 谢谢。它工作正常。我只是想知道您是否可以提供帮助的另一件事是连续读取文件的可能性?我知道可以使用 Get-Content -wait 来完成,但我不太确定是否使用 switch -file ...
    • 我正在尝试这个:switch -regex (Get-Content test.log -Wait)...顺便说一句
    • 似乎不起作用,因为我在其中添加了一个新的文本块...脚本继续运行而不返回任何内容
    • 我想我找到了解决方案:Get-Content test.log -Wait | ForEach-Object { switch -regex ($_)
    猜你喜欢
    • 2013-10-24
    • 1970-01-01
    • 2018-01-15
    • 1970-01-01
    • 1970-01-01
    • 2021-12-05
    • 1970-01-01
    • 2012-04-25
    • 1970-01-01
    相关资源
    最近更新 更多