Utils/wc.ps1
|
Register-GnuUtil @{ Name = 'wc' Category = 'Text processing' Synopsis = 'Print newline, word, and byte counts for each file.' Usage = @('wc [OPTION]... [FILE]...', 'wc [OPTION]... --files0-from=F') Description = @( 'Print newline, word, and byte counts for each FILE, and a total line if', 'more than one FILE is specified. A word is a nonempty sequence of non white', 'space delimited by white space characters or by start or end of input.', '', 'With no FILE, or when FILE is -, read standard input.', '', 'The options below may be used to select which counts are printed, always in', 'the following order: newline, word, character, byte, maximum line length.' ) FileOperands = $true Options = @( @{ Short = 'c'; Long = 'bytes'; Help = 'print the byte counts' } @{ Short = 'm'; Long = 'chars'; Help = 'print the character counts' } @{ Short = 'l'; Long = 'lines'; Help = 'print the newline counts' } @{ Long = 'files0-from'; Arg = 'F'; Help = 'read input from the files specified by NUL-terminated names in file F; If F is - then read names from standard input' } @{ Short = 'L'; Long = 'max-line-length'; Help = 'print the maximum display width' } @{ Short = 'w'; Long = 'words'; Help = 'print the word counts' } @{ Long = 'total'; Arg = 'WHEN'; Help = 'when to print a line with total counts; WHEN can be: auto, always, only, never' } ) Notes = @('PowerShell note: pipeline input counts as UTF-8 text with a LF after every line.') } function script:Measure-GnuText { <# .SYNOPSIS Computes the wc counts of one input. #> param([Parameter(Mandatory)][AllowEmptyCollection()][byte[]]$Bytes) $text = $script:GnuUtf8.GetString($Bytes) $maxWidth = 0 foreach ($line in [regex]::Split($text, "[`r`n`f]")) { $width = 0 foreach ($char in $line.ToCharArray()) { if ($char -eq "`t") { $width += 8 - ($width % 8) } elseif ([char]::IsLowSurrogate($char)) { continue } elseif ([int]$char -ge 32) { $width++ } } if ($width -gt $maxWidth) { $maxWidth = $width } } @{ lines = [long]($text.Length - $text.Replace("`n", '').Length) words = [long][regex]::Matches($text, '\S+').Count chars = [long]($text.Length - [regex]::Matches($text, '[\uDC00-\uDFFF]').Count) bytes = [long]$Bytes.Length 'max-line-length' = [long]$maxWidth } } function script:Invoke-GnuWordCount { <# .SYNOPSIS Implementation of wc. #> param([Parameter(Mandatory)][hashtable]$Context) $total = [string](Get-GnuOptionValue -Context $Context -Key 'total' -Default 'auto') if (@('auto', 'always', 'only', 'never') -notcontains $total) { Write-GnuUsageError -Context $Context -Message "invalid argument '$total' for '--total'`nValid arguments are: 'auto', 'always', 'only', 'never'" return } $keys = @(@('lines', 'words', 'chars', 'bytes', 'max-line-length') | Where-Object { Test-GnuOption -Context $Context -Key $_ }) if ($keys.Count -eq 0) { $keys = @('lines', 'words', 'bytes') } $operands = @($Context.Operands) $showNames = $true if (Test-GnuOption -Context $Context -Key 'files0-from') { if ($operands.Count -gt 0) { Write-GnuUsageError -Context $Context -Message "extra operand '$($operands[0])'`nfile operands cannot be combined with --files0-from" return } $listName = [string](Get-GnuOptionValue -Context $Context -Key 'files0-from') $listText = Get-GnuInputText -Context $Context -Operand $listName if ($null -eq $listText) { return } $operands = @(Split-GnuRecord -Text $listText -Separator "`0") } elseif ($operands.Count -eq 0) { $operands = @('-') $showNames = $false } $results = New-Object 'System.Collections.Generic.List[hashtable]' $minimumWidth = 1 $regularTotal = [long]0 foreach ($operand in $operands) { $bytes = Get-GnuInputBytes -Context $Context -Operand $operand if ($null -eq $bytes) { continue } if ($operand -eq '-') { $minimumWidth = 7 } else { $regularTotal += $bytes.Length } $results.Add(@{ Name = $operand; Counts = (Measure-GnuText -Bytes $bytes) }) } $width = 1 if ($total -ne 'only' -and -not ($keys.Count -eq 1 -and $operands.Count -eq 1)) { $width = [Math]::Max($minimumWidth, ([string]$regularTotal).Length) } $sum = @{} foreach ($key in $keys) { $sum[$key] = [long]0 } foreach ($result in $results) { foreach ($key in $keys) { if ($key -eq 'max-line-length') { $sum[$key] = [Math]::Max($sum[$key], $result.Counts[$key]) } else { $sum[$key] += $result.Counts[$key] } } if ($total -ne 'only') { $line = ($keys | ForEach-Object { ([string]$result.Counts[$_]).PadLeft($width) }) -join ' ' if ($showNames) { $line += " $($result.Name)" } $line } } $printTotal = $total -eq 'always' -or $total -eq 'only' -or ($total -eq 'auto' -and $operands.Count -gt 1) if ($printTotal) { $line = ($keys | ForEach-Object { ([string]$sum[$_]).PadLeft($width) }) -join ' ' if ($total -ne 'only') { $line += ' total' } $line } } |