首页 > 解决方案 > 遇到下一条记录时,使用 powershell 脚本读取上一条记录中的所有行

问题描述

我已经用更详细的描述编辑了帖子。我的 Input.DAT 有 3 条记录,每条记录以“01”开头。

   01 firstN1 lastN1
   02 studentid1 sdf   course1
   03 class1 dfg location1
   05 dfgdf
   01 firstN2 lastN2 
   02 studentid2 ert   568  
   03 class2 dfg location2
   01 firstN3 lastN3 
   03 class3 dfg location3

我的 Powershellscript.ps1 文件:

    foreach-object {  
       $line = $_.tostring()  
      $i= $line.substring(0, 2).trim()    
    
        if($i -eq "01"){  
    $firstname=$line.substring(3,7).trim()  
    $lastname=$line.substring(11,6).trim()}  
   
      
        if($i -eq "02"){  
    $studentid=$line.substring(3,10).trim()
    $course=$line.substring(20,7).trim() }  
      
    
        if($i -eq "03"){  
    $class=$line.substring(3,6).trim()
    $location=$line.substring(14,9).trim()}    
 
if($i -eq "05"){
    ****************}

# need help in below logic   

     Foreach ($x in $1) {   
         if ($x -eq '01') {   
 
    add-content -path outputfile.txt -value   firstname,"|",$lastname,"|",$studentid,"|",$class,"|",$course,"|",$location
 
Clear-Variable -Scope Script firstname*, lastname*, studentid*, class*, course* ,location*

我的问题是:当前代码正在寻找第一个“01”并且只读取数据(01 firstN1 lastN1 234)到输出文件。但是我希望编写逻辑来检查循环何时再次命中第二个“01”或/和 $firstname,当我们知道我们已经完成读取第一组记录时,然后只将所有先前的记录读取到输出文件在单行中。

My Output file(outputfile.txt) looks like this.
       firstname|lastname|studentid|class|course|location
       firstN1|lastN1||||
       firstN2|lastN2|studentid1|class1|course1|location1
       firstN3|lastN3|studentid2|class2|568|location2

Instead of 
       firstname|lastname|studentid|class|course|location
       firstN1|lastN1|studentid1|class1|course1|location1  
       firstN2|lastN2|studentid2|class2|568|location2
       firstN3|lastN3|studentid3|class3|course3|location3

提前致谢。

标签: powershellshellfor-loopif-statement

解决方案


您的输出似乎不可能,因为您的数据不包含 studentid3 或 class3。

除此之外,我推荐一种不同的方法来提取和输出数据。

为了这个例子,我正在创建一个 3 记录文本文件。

$tempfile = New-TemporaryFile

@'
01 firstN1 lastN1 234  
02 studentid1 sdf 345  
03 class1 dfg 456  
01 firstN2 lastN2 567  
02 studentid2 ert 568  
03 class2 dfg 890
01 firstN3 lastN3 012 
02 studentid3 ert 876 
03 class3 dfg 321
'@ | Set-Content $tempfile -Encoding UTF8

如果您知道之后总会有两行,那么您可以使用-Context参数对Select-String每 3 行部分进行分组。

Select-String -Path $tempfile -Pattern '01' -Context 0,2

我们只需匹配 01 并选择匹配行和后面的 2 行。context 0,2

现在,如果我们通过循环发送每组行,Foreach-Object我们就可以操作/解析文本。有几种方法,许多人更喜欢Switch使用-Regex参数。我们制作我们的正则表达式模式来获取所需的文本。如您所见,我们将使用两种不同的 switch 语句,一种用于匹配$_.line,另一种用于$_.Context.PostContext行。在 switch 语句之后,我们应该填充所有四个变量,因此我们将创建一个PSCustomObject然后简单地将整个输出通过管道传递到Export-Csv指定所需的分隔符。

Select-String -Path $tempfile -Pattern '01' -Context 0,2 | ForEach-Object {
    Switch -Regex ($_.Line){
        '01\s{1,}(.+?)\s{1,}(.+?)\s'{$firstname,$lastname = $matches.1,$matches.2}
    }
    Switch -Regex ($_.Context.PostContext){
        '02\s{1,}(.+?)\s'{$studentid = $matches.1}
        '03\s{1,}(.+?)\s'{$classid = $matches.1}
    }
    [PSCustomObject]@{
        FirstName = $firstname
        LastName  = $lastname
        StudentID = $studentid
        ClassID   = $classid
    }
} | Export-Csv -Path outputfile.csv -Delimiter '|' -NoTypeInformation

Csv 格式看起来就像您想要的文本文件,具有列标题的额外好处,并且可以重新导入并用于其他任务。

Get-Content .\outputfile.csv

"FirstName"|"LastName"|"StudentID"|"ClassID"
"firstN1"|"lastN1"|"studentid1"|"class1"
"firstN2"|"lastN2"|"studentid2"|"class2"
"firstN3"|"lastN3"|"studentid3"|"class3"

我只是用Get-Content. 要使用数据,您应该使用Import-Csv和处理对象。对象只是使 powershell 如此强大的原因之一。

正则表达式详细信息

01, 02, 03- 文字匹配

\s{1,} - 一个或多个空格

() - 捕获组

.+? - 匹配一个或多个字符,不贪心

\s - 正好一个空格

编辑

如果像您更新的样本一样,它确实是零星地填充,您很可能最终得到无效数据。第一组会有456的位置,第三组没有学生证。一个更现实的例子肯定会帮助我们帮助你。我不会将其输出到 CSV,因为列不会排列。如果需要,您可以强制使用空白学生 ID,但它似乎更有可能被错误地遗漏了。

$text = @'
   01 firstN1 lastN1 234      dfgh
   02 studentid1 sdf course1 345  
   03 class1 dfg 456                35        dfg
   05 dfgdf dghfg    sdfh                123       45
   01 firstN2 lastN2 567  
   02 studentid2 ert 568  
   03 class2 dfg location2 890
   01 firstN3 lastN3 567  
   03 class3 dfg location3 890
'@

$text -split '(?=01)' | ForEach-Object {
    $ht = [ordered]@{}

    Switch -Regex ($_){
        '01\s{1,}(.+?)\s{1,}(.+?)\s'{$ht.FirstName,$ht.LastName = $matches.1,$matches.2}
        '02\s{1,}(.+?)\s'{$ht.StudentID = $matches.1}
        '03\s{1,}(.+?)\s{1,}\w{3,}\s{1,}(.+?)\s'{$ht.ClassID,$ht.LocationID = $matches.1,$matches.2}
    }

    if($ht.values -ne ''){
        [PSCustomObject]$ht
    }
}

输出

FirstName  : firstN1
LastName   : lastN1
StudentID  : studentid1
ClassID    : class1
LocationID : 456

FirstName  : firstN2
LastName   : lastN2
StudentID  : studentid2
ClassID    : class2
LocationID : location2

FirstName  : firstN3
LastName   : lastN3
ClassID    : class3
LocationID : location3

推荐阅读