There is a great utility called cloc that can be used to count lines of code. It detects the language and thus can distinguish between comments, blank lines and actual code.

It cannot, however, without jumping through some weird hoops, display the sum of those three categories, so I had to whip up a very simple awk script to process the output (it should work in at least zsh and bash). I put it in a function and placed it in my dotfile:

function clocit() {
    cloc --progress-rate=0 "$@" |
    tee >(tail -n 2 |
    awk '{for (i=3; i<=NF; ++i) j+=$i;} END {print "Total: " j}')

A breakdown of every section for clarity:

function clocit() { – declare a function; function is used so as to not bother with escaping needed for using alias;

cloc --progress-rate=0 "$@" – call the cloc app and pass arguments ("$@") from the function call; the --progress-rate=0 part is used to suppress interactive progress output which is undesirable for processing with awk;

tee >(tee is used to display the output of the cloc and simultaneously pipe it further for processing; redirection to a subshell (the >( part here) has to be used since we need to both display the original output and process it further down the pipe;

tail -n 2 – use only the two last lines of the output (sum of lines for all languages); while it still leaves the useless line of dashes, the loop starts from field #3, and there is only one field in the last line, so the loop body is skipped for that line (and awk is also smart enough to ignore non-numerical values when adding);

awk – begin awk script;

{for (i=3; i<=NF; ++i) j+=$i;} – loop through every field, starting from the third one (awk is not zero based, so the index starts at 1), skipping the language name and the number of files per language while adding the amount of blank, comment and code lines to an accumulator variable; NF is the number of fields (i.e. cells) in the line; the loop is executed for every line passed to awk.

While we don’t really need to loop here (we can just sum the fields with $3 + $4 + $5), I’m leaving it to illustrate the usage of loops and the NF built-in variable.

END {print "Total: " j} – print the accumulated value when we have finished processing;

) – close the subshell command;

You can pass parameters to the function to tune it further:

$ clocit --vcs=git --include-lang="JavaScript,PHP"
    3177 text files.
    2772 unique files.
    4602 files ignored. v 1.74  T=17.99 s (64.5 files/s, 22778.2 lines/s)
Language                     files          blank        comment           code
JavaScript                     531          33626          15345         186245
PHP                            630          18278          31766         124473
SUM:                          1161          51904          47111         310718
Total: 409733