1
\$\begingroup\$

I'm often frustrated by the lack of ability to obtain simple size metrics about code projects I'm working on, like total lines of code, or character count. To fix this, I wrote the following PowerShell utility:

Function GetProjectMetrics
{
 [CmdletBinding()]
 Param(
 [Parameter(mandatory=$True)]
 [string]$projectPath,
 [Parameter(mandatory=$True)]
 [string]$fileFilter,
 [switch]$ignorews
 )
 Set-Location $projectPath
 Switch($ignorews)
 {
 $True 
 { 
 Get-ChildItem -recurse -include $fileFilter `
 | Get-Content `
 | Measure-Object -Character -Word -Line -IgnoreWhiteSpace
 Break
 }
 $False
 {
 Get-ChildItem -recurse -include $fileFilter `
 | Get-Content `
 | Measure-Object -Character -Word -Line
 Break
 }
 Default
 {
 Write-Host "Error executing 'GetProjectMetrics'."
 Break
 }
 }
}
New-Alias -Name gpm -Value GetProjectMetrics
New-Alias -Name getpm -Value GetProjectMetrics

The above code is placed within my Microsoft.PowerShell_profile.ps1 file (my PowerShell profile), which gives me the ability to use it straight from the PowerShell command line.

Here's some example usage:

PS C:\Users\Ethan> gpm ".\Documents\MyPythonProject" "*.py" -ignorews
PS C:\Users\Ethan> getpm ".\Documents\MyPythonProject" "*.py"
PS C:\Users\Ethan> GetProjectMetrics ".\OneDrive\CS Projects\CS Tests" "*.cs" -ignorews

What can I improve?

asked Jul 11, 2017 at 23:48
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

My suggestions:

The script (rewritten almost completely):

<#
 SYNOPSIS: Calculates simple size metrics about code projects.
 Combination of `Get-ChildItem`, `Get-Content` and `Measure-Object`
 Parameter Meaning (see native cmdlet)
 --------- ---------------------------
 -Path obvious (Convert-Path, Get-ChildItem, Get-Item, Push-Location)
 -LiteralPath obvious (Convert-Path, Get-ChildItem, Get-Item, Push-Location)
 -Filter obvious (Get-ChildItem)
 -IgnoreWhiteSpace obvious (Measure-Object)
 -Detailed returns size metrics about individual files severally
 -PushLocation side effect: `Push-Location`
 RETURNS: Custom combination of GenericMeasureInfo and TextMeasureInfo:
 Name Type Meaning
 ---- ---- -------
 Count [int] number of files of given pattern in given (literal)path
 Lines [int] number of lines
 Words [int] number of words
 Characters [int] number of characters
 Property [string] tested path
#>
Set-StrictMode -Version latest
Function Measure-Project
{
 [CmdletBinding(DefaultParameterSetName='Items')]
 Param(
 [Alias('projectPath')]
 [Parameter(Mandatory=$false,
 Position=0,
 ParameterSetName='Items')]
 [string]$Path='.\',
 [Parameter(Mandatory=$false,
 Position=0,
 ParameterSetName='LiteralItems')]
 [string]$LiteralPath='.\',
 [Alias('fileFilter')]
 [Parameter(Mandatory=$false,
 Position=1)]
 [string]$Filter='*.ps1',
 [Alias('IgnoreWS')]
 [switch]$IgnoreWhiteSpace,
 [Alias('PushD')]
 [switch]$PushLocation,
 [switch]$Detailed
 )
 Function out ($Cnt, $Prm, $Obj) {
 $auxCounter = $Obj | Measure-Object -Character -Word -Line @auxParMO
 $auxCounter | Add-Member -NotePropertyName Count -NotePropertyValue $Cnt
 $auxCounter.Property = $Prm
 return $auxCounter
 }
 if ( $PSCmdlet.ParameterSetName -eq 'LiteralItems') {
 $auxParTP = @{ LiteralPath = $LiteralPath }
 } else {
 $auxParTP = @{ Path = $Path }
 }
 if ( Test-Path @auxParTP ) {
 $auxPath = Convert-Path @auxParTP
 if ( (Get-Item @auxParTP) -is [System.IO.DirectoryInfo]) {
 $auxFiles = Get-ChildItem @auxParTP -Filter $Filter -Recurse -File
 $auxParMO = @{ IgnoreWhiteSpace = $IgnoreWhiteSpace.IsPresent } 
 # $PSBoundParameters.ContainsKey('IgnoreWhiteSpace')
 if ($Detailed.IsPresent -and $auxFiles) {
 $auxFiles | ForEach-Object {
 out -Cnt 1 -Prm $_.FullName -Obj ($_ | Get-Content)
 }
 } else {
 $auxPar = Join-Path -Path $auxPath -ChildPath $Filter
 if ( $auxFiles ) {
 if ( $auxFiles -is [array] ) {
 $auxCnt = $auxFiles.Count
 } else {
 $auxCnt = 1
 }
 out -Cnt $auxCnt -Prm $auxPar -Obj ($auxFiles | Get-Content)
 } else {
 Write-Warning "No '$Filter' inside '$Path'"
 out -Cnt 0 -Prm $auxPar -Obj ''
 }
 }
 if ( $PushLocation.IsPresent ) {Push-Location @auxParTP}
 } else {
 $auxMsg = "'$($auxParTP.Values.GetEnumerator())' is not a directory"
 Write-Error -Message $auxMsg -Category InvalidArgument
 }
 }
 else
 {
 $auxMsg = "Cannot find path '$($auxParTP.Values.GetEnumerator())'"
 Write-Error -Message $auxMsg -Category ObjectNotFound
 }
}

Sample output demonstrates necessity of -Encoding parameter as both files contain nearly the same text so the utility should show the same count of characters. Of course, I'd expect the same encoding of source files in a particular project so sorry for the following simplified example:

PS D:\PSh> Measure-Project -LiteralPath 'D:\bat\[source]\XX\' -filter * -Detailed | ft
Count Lines Words Characters Property 
----- ----- ----- ---------- -------- 
 1 1 5 118 D:\bat\[source]\XX\[sourcefile].log
 1 1 5 59 D:\bat\[source]\XX\[sourcefile].txt
PS D:\PSh> type -LiteralPath 'D:\bat\[source]\XX\[sourcefile].txt'
Toto je obsah souboru `D:\bat\[source]\XX\[sourcefile].txt`
PS D:\PSh> type -LiteralPath 'D:\bat\[source]\XX\[sourcefile].log' -Encoding Unicode
Toto je obsah souboru `D:\bat\[source]\XX\[sourcefile].log`
answered Jul 27, 2017 at 16:29
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.