let $((total = total + count ))
This works, but is a bit redundant, since both let
and $(( .. ))
start arithmetic expansion.
Any of let "total = total + count"
, let "total += count"
, : $((total = total + count))
or total=$((total + count))
would do it without the duplication. The last two should be compatible with a standard shell, let
isn't.
total=0
find /home -type f -name "*.txt" | while read -r FILE; do
total=...
done
echo TOTAL LINES COUNTED: $total
You didn't say what problem you mean, but one problem you have here is that in Bash, the parts of a pipeline run in subshells by default, so any changes made to total
inside the while
loop are not visible after it. See: Why is my variable local in one 'while read' loop, but not in another seemingly similar loop?
You could use shopt -s lastpipe
to have the last part of the pipeline run in the shell; or group the while
and echo
:
find ... | { while ...
done; echo "$total"; }
Of course, find ... | while read -r FILE;
will have problems with filenames that contain newlines, or start/end with whitespace. You could fix that with
find ... -print0 | while IFS= read -r -d '' FILE; do ...
or, if you don't care about the breakdown of per-file line counts and know your files are complete text files, with none missing the final newline, you could simply concatenate all the files together and run wc -l
on that.
If your files may be missing the newline at the end of the last line, and you want to count that final incomplete line, then you can't do that, and need to keep using grep -c ^
instead of wc -l
. (Counting the final partial line is pretty much the only reason to use grep -c ^
instead of wc -l
.)
See: What's the point in adding a new line to the end of a file? and Why should text files end with a newline? on SO.
Also, if you only want the total count, all the files matching the pattern are regular files (so the -type f
test can be dropped), and you have Bash and GNU grep, you could also do:
shopt -s globstar
shopt -s dotglob
grep -h -c ^ **/*.txt | awk '{ a += $0 } END { print a }'
**/*.txt
is a recursive glob, it needs to be explicitly enabled to work. dotglob
makes that glob also match filenames starting with a dot. grep -h
suppresses the filenames from the output, and the awk
script counts the sum. Since no filenames are printed, this should work even if some of them a problematic.
Or, as suggested by @fra-san, based on another now-deleted answer:
grep -r -c -h --include='*.sh' ^ |awk '{ a+= $0 } END {print a }'