views:

2269

answers:

4

I think we have a bunch of commented out code in our source, and rather than delete it immediately, we've just left it. Now I would like to do some cleanup.

So assuming that I have a good enough RegEx to find comments (the RegEx below is simple and I could expand on it based on our coding standards), how do I take the results of the file that I read up and output the following:

  • Filename
  • Line Number
  • The actual line of code

I think I have the basis of an answer here, but I don't know how to take the file that I've read up and parsed with RegEx and spit it out in this format.

I'm not looking for the perfect solution - I just want to find big blocks of commented out code. By looking at the result and seeing a bunch of files with the same name and sequential line numbers, I should be able to do this.

$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?
}
+1  A: 

I would look at doing something like:

dir $location -inc *.cs -rec | `
  %{ $file = $_; $n = 0; get-content $_ } | `
  %{ $_.FileName = $file; $_.Line = ++$n; $_ } | `
  ?{ $_ -match $regex } | `
  %{ "{0}:{1}: {2}" -f ($_.FileName, $_.Line, $_)}

I.e. add extra properties to the string to specify the filename and line number, which can be carried through the pipeline after the regex match.

(Using ForEach-Object's -begin/-end script blocks should be able to simplify this.)

Richard
I'm not sure what ?{ $_._ -match $regex } | ` is doing, but that is the line that seems to prevent me from getting results. What does that do?Also, I had to change $_.FileName and $_.Line to $FileName and $Line to get it to run
Macho Matt
@Macho: Typo... will ix, should be just $_.
Richard
Note other answers: select-string already captures the filename and line number.
Richard
+5  A: 

You could do:

dir c:\codeishere -filter *.cs -recurse | select-string -Pattern '//.*;' | select Line,LineNumber,Filename
Shay Levy
+2  A: 
gci c:\codeishere *.cs -r | select-string "//.*;"

The select-string cmdlet already does exactly what you're asking for, though the filename displayed is a relative path.

Lee
+2  A: 

I would go personally even further. I would like to compute number of consecutive following lines. Then print the file name, count of lines and the lines itself. You may sort the result by count of lines (candidates for delete?). Note that my code doesn't count with empty lines between commented lines, so this part is considered as two blocks of commented code:

// int a = 10;
// int b = 20;

// DoSomething()
// SomethingAgain()

Here is my code.

$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 $_ }
}

If you have any idea how to improve the code, post it! I have a feeling that I could do it using some standard cmdlets and the code could be shorter..

stej
This was beyond the scope of the question, but wow... this was awesome. Thanks!!!! I'll be using this one.
Macho Matt
Yes, beyond the scope, but I thought it could be useful. Besides that it was fun :) If you were interested in matching the empty lines between blocks as well, let me know. I would try to adapt the script.
stej