8

I use the following Bash Shell script to list the ".txt" files recursively under the current directory :

#!/bin/bash
for file in $( find . -type f -name "*.txt" )
do
  echo $file
  # Do something else.
done

However, some of the ".txt" files under the current directory have spaces in their names, e.g. "my testing.txt". The listing becomes corrupted, e.g. "my testing.txt" is listed as

my
testing.txt

It seems that the "for" loop uses "white space" (space, \n etc) to separate the file list but in my case I want to use only "\n" to separate the file list.

Is there any way I could modify this script to achieve this purpose. Any idea.

Thanks in advance.

user1129812
  • 2,859
  • 8
  • 32
  • 45

2 Answers2

8

If you're using bash 4, just use a glob:

#!/bin/bash                                                                     

shopt -s globstar

for file in **/*.txt
do
  if [[ ! -f "$file" ]]
  then
      continue
  fi
  echo "$file"
  # Do something else.                                                          
done

Be sure to quote "$file" or you'll have the same problem elsewhere. ** will recursively match files and directories if you have enabled globstar.

FatalError
  • 52,695
  • 14
  • 99
  • 116
7

Yes, have find separate the file names with NUL and use read to delimit on NUL. This will successfully iterate over any file name since NUL is not a valid character for a file name.

#!/bin/bash
while IFS= read -r -d '' file; do
  echo "$file"
  # Do something else.
done < <(find . -type f -name "*.txt" -print0)

Alternatively, if the # do something else is not too complex, you can use find's -exec option and not have to worry about proper delimiting

SiegeX
  • 135,741
  • 24
  • 144
  • 154
  • Thanks. But the method seems less familiar to me. I'll try it later. – user1129812 Feb 04 '12 at 08:17
  • 1
    The idiomatic use of `find` is something like this. The problem with `for file in $(find ...)` is precisely that it will split on whitespace. Instead, use `find ... | while read file`. The additional options to `read` are required to properly cope with even file names with newlines, but if all you need to cope with is spaces, you can be a bit flimsier. – tripleee Feb 04 '12 at 10:52
  • This method is a good alternative solution for my case as it allows arbitrary allowable file names. The method is now tested OK and works for my case too. – user1129812 Feb 04 '12 at 15:03
  • 1
    @user1129812 To add what @tripleee said, the reason why I didn't pipe the output of `find` but instead used process substitution `<()` is because the latter doesn't create a subshell so any variables you might alter inside the while-loop can be accessed outside. This may be important if say you want to keep a counter of how many files you iterated through and access that counter elsewhere in the script. – SiegeX Feb 04 '12 at 19:12
  • 1
    @SiegeX I see your point. `find ... | while read file` would leave the `while` loop in a subshell whose variables could not be seen in the calling shell. Anyway, both solution works for my case. Thanks SiegeX and tripleee. – user1129812 Feb 05 '12 at 02:19