Recursively count lines of code, excluding empty lines and comments

PowerShell, 47 46 bytes

ls *.sh -r|gc|%{$o+=!($_-match'^\s*(#|$)')};$o

Try it online! (will always return 0 since there aren't any files)

Try it online! (here's a link that populates a dummy file so you can see the process)

-1 byte thanks to Neil

ls is alias for Get-ChildItem, specifying *.sh with the -recurse parameter, then we get-content of files. For each of those lines |%{...}, we accumulate into our $output a one if the Boolean !(...) statement is truthy. Inside the statement is a simple regex -match against the whitespace-only/comment/blank lines. Finally we leave $o on the pipeline.

The implicit Write-Output that happens at program completion adds a trailing newline, but that shouldn't matter in this case because the variable $o itself doesn't have a trailing newline nor does the actual return variable. It's a quirk of the shell, not a quirk of the program. For example, saving this to a script and executing that script in a pipeline will not have a newline.


Powershell 3+, 40 byte

(ls *.sh -r|sls '^\s*(#|$)' -a -n).Count

ls *.sh -r gets a file names from the directory tree. sls (alias for Select-String) gets all strings (-a is shortcut for -AllMatches) that not mathces (-n is alias for -NotMatch) to the pattern '^\s*(#|$)'.


R, 70 64 bytes

sum(!grepl("^(#|$)",unlist(lapply(dir(,".sh$",r=T),readLines))))

Explanation:

The dir function has the recursive flag set.

readLines returns the lines of a file in a vector, which are then flattened with unlist.