【问题标题】:PowerShell script that searches for a string in a .txt and if it finds it, looks for the next line containing another string and does a job with it在 .txt 中搜索字符串的 PowerShell 脚本,如果找到,则查找包含另一个字符串的下一行并对其进行处理
【发布时间】:2021-08-31 09:13:55
【问题描述】:

我有电话

Select-String -Path ".\*.txt" -Pattern "6,16" -Context 20 | Select-Object -First 1

这将返回 20 行上下文以寻找“6,16”的模式。

我需要在“6,16”行之后查找包含字符串“ID 号:”的下一行,阅读“ID 号:”旁边的文本是什么,查找此确切文本是否存在于另一个位于同一文件夹中的“export.txt”文件(因此在“.\export.txt”中),并查看它是否在包含相关文本的行中包含“6,16”。

我知道这可能看起来令人困惑,但我的意思是例如:

example.txt:5218: ID number:0002743284

显示这是否是真的:

export.txt:9783: 0002743284 *some text on the same line for example* 6,16

【问题讨论】:

  • 在帖子中您说您需要“在同一行”找到相同的文本,但在示例文本中它显然是两个不同的行(第 5218 行与第 9783 行)- 是哪一个?
  • @MathiasR.Jessen:涉及两个文件,行号对应不同的文件。这个问题可以再充实一点,但要点是:“在一个文件中找到一个模式,从匹配后的 20 行中提取一个子字符串,然后在第二个文件中找到提取的子字符串,然后是原始模式同一行。”

标签: powershell text


【解决方案1】:

如果我正确理解了这个问题,您正在寻找类似的内容:

Select-String -List -Path *.txt -Pattern '\b6,16\b' -Context 0, 20 |
  ForEach-Object {
    if ($_.Context.PostContext -join "`n" -match '\bID number:(\d+)') {
      Select-String -List -LiteralPath export.txt -Pattern "$($Matches[1]).+$($_.Pattern)"
    }
  }
  • Select-String-List 开关将匹配限制为每个输入文件的一个 匹配; -Context 0,20 还包括20输出中匹配的行(但没有(0之前)。

    • 请注意,我已将 \bword-boundary assertion 置于搜索模式的任一端 6,16,以排除意外误报,例如 96,169
  • $_.Context.PostContext 包含匹配行之后的行数组(它本身存储在$_.Line 中):

    • -join "`n" 将它们连接成一个多行字符串,以保证后续-match 操作在automatic $Matches variable 中报告捕获的结果,特别是在$Matches[1] 中报告感兴趣的ID 号,由捕获的文本第一个(也是唯一一个)捕获组 ((\d+))。
  • 然后将捕获的 ID 与原始搜索模式结合使用,形成一个正则表达式,在同一行上查找两者,并传递给第二个 Select-String 调用,该调用通过 export.txt 进行搜索

    • 注意:表示匹配行的对象,如果有的话,默认输出;要仅返回 $true$false,请将 -List 替换为 -Quiet

【讨论】:

  • 很高兴听到它有帮助,@Sv3n;我的荣幸。至于你的问题:使用\b6,16\b - 详情请查看我的更新。
  • @Sv3n, Select-String 使用与-match 运算符相同的正则表达式匹配,并且'96,16' -match '\b6,16\b''16,16' -match '\b6,16\b' 返回$false,而' 6,16' -match '\b6,16\b' 返回$true。 /跨度>
【解决方案2】:

您所期望的和您尝试过的代码有很多错误,所以让我们分解它并找到解决方案。感谢您自己尝试此操作。首先,这是解决方案,请阅读以下代码以了解您做错了什么以及如何获得我编写的代码:

# Get matching lines plus the following line from the example.txt seed file
$seedMatches = Select-String -Path .\example.txt -Pattern "6,\s*16" -Context 0, 2

# Obtain the ID number from the line following each match
$idNumbers = foreach( $match in $seedMatches ) {
  $postMatchFields = $match.Context.PostContext -split ":\s*"

  # Note: .IndexOf(object) is case-sensitive when looking for strings
  # Returns -1 if not found
  $idFieldIndex = $postMatchFields.IndexOf("ID number")

  # Return the "ID number" to `$idNumbers` if "ID number" is found in $postMatchFields
  if( $idFieldIndex -gt -1 ) {
    $postMatchFields[$idFieldIndex + 1]
  }
}

# Match lines in export.txt where both the $id and "6,16" appear
$exportMatches = foreach( $id in $idNumbers ) {
  Select-String -Path .\export.txt -Pattern "^(?=.*\b$id\b)(?=.*\b6,\s*16\b).*$"
}

mklement0's answer 基本上将其压缩为更少的代码,但我想将其完全分解。


首先,Select-String -Path ".\*.txt" 将查看当前目录中的所有.txt 文件。您需要将其缩小到您在种子文件中寻找的特定命名模式(我们想要在其他文件中查找要查找的 ID 的文件)。对于此示例,我将使用 example.txtexport.txt 作为您在问题中其他地方使用的路径,而不使用通配符来匹配文件名。

接下来,-Context 给出了比赛中周围线条的上下文。您只关心下一行匹配,因此 0, 1 应该足以满足 -Context(匹配前 0 行,匹配后 1 行)。

最后,我将\s* 添加到-Pattern 以匹配空格,如果16 曾经从, 填充。所以现在我们准备好了Select-String 命令:

$seedMatches = Select-String -Path .\example.txt -Pattern "6,\s*16" -Context 0, 2

接下来,我们需要遍历种子文件中的匹配结果。您可以使用foreachForEach-Object,但我将在下面的示例中使用foreach

对于$seedMatches 中的每个$match,我们需要从每个匹配项之后的行中获取$idNumbers。当$matchToString()'d 时,它会吐出匹配的行和任何周围的上下文行。由于我们的上下文匹配后只有一行,因此我们可以为此获取 $match.Context.PostContext

现在我们可以获得$idNumber。我们可以通过使用-split 操作符将:\s* 模式上的字符串拆分(\s* 匹配任何或不匹配空格)将example.txt:5218: ID number:0002743284 拆分为字符串数组。一旦我们有了这个,我们就可以得到“ID号”的索引,并得到紧随其后的字段的值。现在我们有了$idNumbers。我还将在下面添加一些保护措施,以确保在继续之前确实找到了 ID numbers 字段。

$idNumbers = foreach( $match in $seedMatches ) {
  $postMatchFields = $match.Context.PostContext -split ":\s*"

  # Note: .IndexOf(object) is case-sensitive when looking for strings
  # Returns -1 if not found
  $idFieldIndex = $postMatchFields.IndexOf("ID number")

  # Return the "ID number" to `$idNumbers` if "ID number" is found in $postMatchFields
  if( $idFieldIndex -gt -1 ) {
    $postMatchFields[$idFieldIndex + 1]
  }
}

现在我们有了$idNumbers,我们可以在export.txt 中查找同一行中的ID 号“6,\s*16”,再次使用Select-String。这一次,我先把代码放在前面,因为它不是什么新鲜事,然后解释一下正则表达式:

$exportMatches = foreach( $id in $idNumbers ) {
  Select-String -Path .\export.txt -Pattern "^(?=.*\b$id\b)(?=.*\b6,\s*16\b).*$"
}

$exportMatches 现在将包含在同一行上同时包含目标 ID number6,16 值的行。请注意,未指定顺序,因此表达式使用positive lookaheads 来查找$id6,16 值,而不管它们在字符串中的顺序如何。我不会分解确切的表达式,但如果您将^(?=.*\b0123456789\b)(?=.*\b6,\s*16\b).*$ 插入https://regexr.com,它将分解并详细解释正则表达式模式。


完整代码在上面at the top of this answer

【讨论】:

    猜你喜欢
    • 2011-01-01
    • 2011-09-28
    • 2017-06-16
    • 1970-01-01
    • 2014-10-17
    • 1970-01-01
    • 1970-01-01
    • 2023-03-26
    • 1970-01-01
    相关资源
    最近更新 更多