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.

I've seen various topics on how to iterate through multiple file extensions but in most case, the list is defined.

example:

for file in ${arg}/**/*.{txt,h,py}
do
    ....
done

As can be seen, .TXT files would be ignored. And sadly, the guy who answered it says it only works for bash4. but my setup uses bash3.x.

Any ideas on how it can be done?

share|improve this question
    
    
Use the find command to select the files on any depth, then its -exec option to do something on them. No need to use bash. –  Anthon Jul 23 at 5:28
2  
@Tejas, that is not the same question at all. –  Stéphane Chazelas Jul 23 at 6:58
1  
Could you please edit and clarify your question? It does indeed seem very similar to your previous one. What are you actually trying to achieve? What would ${arg} be here? Do you need recursion? What kind of directory structure would you have? What guy answered it? What did this guy answer? You say that in most cases the list is defined, is it defined in your case? –  terdon Jul 23 at 11:21

2 Answers 2

In bash you can set nocaseglob:

shopt -s nocaseglob

for file in "$arg"/**/*.{txt,h,py}
do
  ....
done

shopt -u nocaseglob

noclaseglob is fine to use in any bash since 2.01, however ** requires bash or later (an it follows symlinks up to bash 4.3). Note the correction to quote $arg since there will be problems if this contains spaces or glob characters.

Without using ** you can do something like:

find "$arg" \( -iname '*.txt' -o -iname '*.h' -o -iname '*.py' \) -exec bash -c '
  for file; do
    ...
  done' bash {} +

This will find all the files you are looking for and pass them as separate arguments to a new bash instance, which will then loop through them. If your per file operation is a single command, you can probably skip the new bash instance and use the command directly.

Update

As per Stephane's comments below, the POSIX compatible solution is:

find "$arg" \( -name '*.[tT][xX][tT]' -o -name '*.[hH]' -o -name '*.[pP][yY]' \) \
  -exec bash -c '
  for file; do
    ...
  done' bash {} +
share|improve this answer
2  
** appeared in bash 4 (borrowed from ksh93, itself borrowing it with differences from zsh). The following of symlinks was fixed in 4.3. You need shopt -s globstar for it. –  Stéphane Chazelas Jul 23 at 6:12
    
Note that -iname is not standard. You also need braces as -a has precedence over -o and so only .py files will be processed. Also note that find will include hidden files (and descend into hidden dirs) while **/ will not by default. –  Stéphane Chazelas Jul 23 at 12:45

Use zsh instead:

setopt extendedglob
for f ($arg/**/*.(#i)(txt|h|py)(N.)) {
  ...
}

(the (N.) is to not return an error if there's no matching file and select regular files only (the equivalent of find's -type f))

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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