Bash Shell list files using "for" loop

19,837

Solution 1

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.

Solution 2

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

Share:
19,837
user1129812
Author by

user1129812

Updated on June 15, 2022

Comments

  • user1129812
    user1129812 almost 2 years

    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
    user1129812 about 12 years
    Thanks. But the method seems less familiar to me. I'll try it later.
  • tripleee
    tripleee about 12 years
    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.
  • user1129812
    user1129812 about 12 years
    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.
  • SiegeX
    SiegeX about 12 years
    @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.
  • user1129812
    user1129812 about 12 years
    @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.