0

I have this dir structure:

ParentDir
 |
 -- Child Dir1
     |
     -- File One.txt
     -- File
 |
 -- Child Dir2
     |
     -- File Two.txt
     -- File

I'm trying to find out all the .txt files and count the sum total of number of lines in them, so I do something like this:

TXTFILES=$(find . -name '*.txt')
cat $TXTFILES | wc -l

but the problem is, I get,

cat: ./Child: No such file or directory
cat: Dir2/File: No such file or directory
cat: Two.txt: No such file or directory
cat: ./Child: No such file or directory
cat: Dir1/File: No such file or directory
cat: One.txt: No such file or directory

How do I handle spaces in the Dir & File names in such a situation? Is it possible without using a loop?

0

4 Answers 4

4

If you don't need to store the file names in a variable, you can do this in 1 line:

find . -name '*.txt' -print0 | xargs -0 wc -l

find supports a -print0 option that makes it use a null byte, rather than a space, to separate the file names. xargs supports a -0 (hyphen zero) switch to tell it the incoming file names are separated by a null byte rather than a space.

2
  • 2
    ... | xargs -0 cat -- | wc -l would produce output more like what the OP is looking for.
    – Kenster
    Commented Aug 1, 2014 at 12:08
  • @Kenster - Yes, perfect. Commented Aug 1, 2014 at 12:09
3
find . -name '*.txt' -exec cat -- '{}' ';' | wc -l

This runs cat individually for each file (which is less than ideal), but the filename is passed directly from find to cat without being parsed by the shell. So the filename could be anything that's problematic on the command line.

2
  • 2
    if you use + instead of ; it'll batch up the arguments to cat as far as possible. Commented Aug 1, 2014 at 12:06
  • This works, with the exact same output I was hoping for, and in the exact format I was expecting. :) Commented Aug 1, 2014 at 12:08
0

dirty and quick:

ls -1FR|grep -c '\.txt$'

zsh:

ls **/*.txt|wc -l
4
  • The second will work in bash 4.x as well if shopt -s globstar is used, although in either shell you can (and should) use wc -l **/*.txt instead.
    – chepner
    Commented Aug 1, 2014 at 14:01
  • @chepner wc -l **/*.txt does something else than ls **/*.txt|wc -l, doesn't it?
    – Kent
    Commented Aug 1, 2014 at 14:05
  • wc -l **/*.txt and cat **/*.txt | wc -l are slightly different, although the total line length of all the files is present in the output. ls **/*.txt | wc -l is quite different from what the OP asked; it gives the number of matching files.
    – chepner
    Commented Aug 1, 2014 at 14:07
  • @chepner ah!!!! I misunderstood the question. I thought he wanted to have the result ls ..|wc -l ....my bad... pending to remove
    – Kent
    Commented Aug 1, 2014 at 14:10
0

You can surround your files name path by " " like this

find . -name '*.txt' | sed -e 's/^/"/g' -e 's/$/"/g' | wc -l

Example :

prompt$ touch a.txt "a a.txt"

prompt$ find . -name '*.txt' | sed -e 's/^/"/g' -e 's/$/"/g'
"./a a.txt"
"./a.txt"

prompt$ find . -name '*.txt' | sed -e 's/^/"/g' -e 's/$/"/g' | wc -l
2

Not the answer you're looking for? Browse other questions tagged or ask your own question.