Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

This question already has an answer here:

A simple for loop in Unix would be:

for FILE in $BASE_WORK_DIR/*.pdf
   do
     echo $FILE
   done

This will echo all .pdf files inside BASE_WORK_DIR directory.

What if BASE_WORK_DIR contains sub directories as well which also contain the pdf file.

In that case how can I design my for loop to take all pdf files from BASE_WORK_DIR as well as sub directories of BASE_WORK_DIR ?

share|improve this question
1  
All the methods you should use are covered here: Looping through files with spaces in the names?. –  slm Jan 22 at 14:01
    
The fact that this question is also answered there doesn't mean that it's the same question -- to the contrary, in fact. Leaving this open. –  Shadur Jan 22 at 17:59
add comment

marked as duplicate by Anthon, Zelda, slm, strugee, Karlson Jan 22 at 20:25

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

3 Answers

In bash4, and following symlinks to directories is desirable, you can enable globstar and use **:

shopt -s globstar
for file in "$base_work_dir"/**/*.pdf
do
  echo "$file"
done

Otherwise in an sh script, find is probably the best way:

IFS='
'
set -f
for file in $(find "$base_work_dir" -name *.pdf)
do
  echo "$file"
done

(add the -L option to find to follow symlinks like with bash globstar)

Note that you will have issues here if any filenames contain newlines.

share|improve this answer
    
Better use ksh93 -o globstar or zsh whose ** don't have the symlink issue. –  Stephane Chazelas Jan 22 at 15:26
    
@Stephane why is the set -f necessary when the *.pdf is quoted? –  Graeme Jan 22 at 15:39
    
In every shell but zsh globbing is performed upon command substitution, so for instance if there's a file called *.pdf in $base_work_dir/foo, all the PDFs in foo except *.pdf will be counted twice. Leaving a variable or command substitution unquoted is the split+glob operator. If you don't want the glob part, you need set -f. –  Stephane Chazelas Jan 22 at 15:45
add comment

The standard and canonical and reliable syntax is:

find . -type f -name '*.pdf' -exec sh -c '
  for f do
    something with "$f"
  done' sh {} +

(note that it may run several sh invocation if the list of files is very big).

With zsh, the equivalent would be

for f (./**/*(.NDoN)) {
  something with "$f"
}

(. for -type f, D to include hidden files, oN to not bother sorting the list, N to not complain if there's no matching file), except that you wouldn't get error messages for the directories you don't have access to.

share|improve this answer
    
Surely the caveat of the sh version is that you cannot set variables in the main part of the script from inside the loop? –  Graeme Jan 22 at 15:59
    
@Graeme, yes, though you could pass them back to the calling shell using export -p. –  Stephane Chazelas Jan 22 at 16:12
add comment

Another solution with find that would solve the problem of newlines in file names:

find "$BASE_WORK_DIR" -name '*.pdf' -print0 |
  while IFS= read -d '' -r file;do
    # your magic here
  done

Caveat emptor: this only works in Zsh and Bash.

-print0 only works in a few find implementations like GNU find. Use -exec printf '%s\0' {} + if your find doesn't support it.

share|improve this answer
    
The $'\0' and -d option for read will work in bash only. –  Graeme Jan 22 at 13:11
    
Lol, that would have been an interesting one to debug further down the line. –  Graeme Jan 22 at 13:21
    
@slm I did not know that. Thanks :). –  Joseph R. Jan 22 at 14:03
1  
@JosephR, bash doesn't support passing NUL characters in arguments (contrary to zsh), $'\0' _is_ '' in bash. –  Stephane Chazelas Jan 22 at 15:21
add comment

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