使用Powershell打印与正则表达式匹配的代码行号

9
我认为我们的源代码中有很多被注释掉的代码,我们没有立即删除它,而是保留了下来。现在我想做一些清理工作。
假设我有足够好的正则表达式来查找注释(下面的正则表达式很简单,我可以根据我们的编码标准进行扩展),如何获取我读取并解析了的文件的结果,并输出以下内容:
- 文件名 - 行号 - 实际代码行
我认为我已经有了一个答案的基础,但我不知道如何将我读取并使用正则表达式解析的文件以这种格式输出。
我不是要寻找完美的解决方案 - 我只想找到大块的被注释掉的代码。通过查看结果并看到一堆文件具有相同的名称和连续的行号,我应该能够做到这一点。
$Location = "c:\codeishere"

[regex]$Regex = "//.*;" #simple example - Will expand on this...

$Files = get-ChildItem $Location -include *cs -recurse
foreach ($File in $Files) {
    $contents = get-Content $File
    $Regex.Matches($contents) | WHAT GOES HERE?
}
4个回答

18

你可以这样做:

dir c:\codeishere -filter *.cs -recurse | select-string -Pattern '//.*;' | select Line,LineNumber,Filename

有没有办法让我将 LineNumber 的实际整数存储到一个变量中,而不必使用文本 LineNumber ________ 等 - Kellen Stuart

2
我个人认为可以更进一步。我希望计算连续的行数。然后输出文件名、行数以及这些行本身。您可以按行数对结果进行排序(可能是要删除的候选项?)。
请注意,我的代码不会将注释行之间的空行计入其中,因此该部分被视为两个注释代码块:
// int a = 10;
// int b = 20;

// DoSomething()
// SomethingAgain()

这是我的代码。

$Location = "c:\codeishere"

$occurences = get-ChildItem $Location *cs -recurse | select-string '//.*;'
$grouped = $occurences | group FileName

function Compute([Microsoft.PowerShell.Commands.MatchInfo[]]$lines) {
  $local:lastLineNum = $null
  $local:lastLine = $null
  $local:blocks = @()
  $local:newBlock = $null
  $lines | 
    % { 
      if (!$lastLineNum) {                             # first line
        $lastLineNum = -2                              # some number so that the following if is $true (-2 and lower)
      }

      if ($_.LineNumber - $lastLineNum -gt 1) {        #new block of commented code
        if ($newBlock) { $blocks += $newBlock }
        $newBlock = $null
      }
      else {                                           # two consecutive lines of commented code
        if (!$newBlock) { 
          $newBlock = '' | select File,StartLine,CountOfLines,Lines
          $newBlock.File, $newBlock.StartLine, $newBlock.CountOfLines, $newBlock.Lines = $_.Filename,($_.LineNumber-1),2, @($lastLine,$_.Line)
        }
        else {
          $newBlock.CountOfLines += 1
          $newBlock.Lines += $_.Line
        }
      }
      $lastLineNum=$_.LineNumber
      $lastLine = $_.Line
    }

  if ($newBlock) { $blocks += $newBlock }
  $blocks
}

# foreach GroupInfo objects from group cmdlet
# get Group collection and compute 
$result = $grouped | % { Compute $_.Group }

#how to print
$result | % {
  write-host "`nFile $($_.File), line $($_.StartLine), count of lines: $($_.CountOfLines)" -foreground Green
  $_.Lines | % { write-host $_ }
}

# you may sort it by count of lines:
$result2 = $result | sort CountOfLines -desc
$result2 | % {
  write-host "`nFile $($_.File), line $($_.StartLine), count of lines: $($_.CountOfLines)" -foreground Green
  $_.Lines | % { write-host $_ }
}

如果您有任何改进代码的想法,请发布!我感觉可以使用一些标准 cmdlet 来完成,这样代码就可以更短。

这超出了问题的范围,但哇...这太棒了。谢谢!!!我会使用这个的。 - Macho Matt
是的,超出了范围,但我觉得它可能有用。除此之外,这还很有趣 :) 如果您对匹配块之间的空行也感兴趣,请告诉我。我会尝试调整脚本。 - stej

2
gci c:\codeishere *.cs -r | select-string "//.*;"
select-string 命令已经完全满足您的要求,但显示的文件名是相对路径。

1
我会考虑做类似这样的事情:
dir $location -inc *.cs -rec | `
  %{ $file = $_; $n = 0; get-content $_ } | `
  %{ $_.FileName = $file; $_.Line = ++$n; $_ } | `
  ?{ $_ -match $regex } | `
  %{ "{0}:{1}: {2}" -f ($_.FileName, $_.Line, $_)}

即在字符串中添加额外的属性以指定文件名和行号,这些属性可以在正则表达式匹配后通过管道传递。

(使用ForEach-Object的-begin/-end脚本块应该能够简化此过程。)


我不确定 "?{ $. -match $regex } | `" 在做什么,但这似乎是阻止我获得结果的那一行。它是做什么的?另外,我不得不将 $.FileName 和 $.Line 更改为 $FileName 和 $Line 才能使其运行。 - Macho Matt
@Macho:打错了,应该是 $_,不是 will ix。 - Richard
注意其他答案:select-string 已经捕获了文件名和行号。 - Richard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接