2

How to change this:

for file in "$arg"/*.{jpg,jpeg,png} ; do
    echo "$file" > z.txt
done

To:

for file in "$arg1"/*.{$arg2} ; do
    echo "$file" > z.txt
done

Since I will get the extension list as a single (comma separated) argument. The above attempt doesn't work.

oblitum
  • 11,380
  • 6
  • 54
  • 120
  • by the look of it I think this will work – Inder Jul 24 '18 at 20:17
  • 5
    @Inder It won't. Brace expansion is done **before** variable expansion. – PesaThe Jul 24 '18 at 20:17
  • 1
    BTW, do you want `z.txt` to have at most only one name? If you want it to have *all* resulting names, put the `> z.txt` after the `done`, so you're opening and truncating the file only once, at the very start of the loop, and leaving it open until the loop ends. – Charles Duffy Jul 24 '18 at 20:26
  • 1
    Possible duplicate of [Matching files with various extensions using for loop](https://stackoverflow.com/q/6223817/608639), [for loop for multiple extension and do something with each file](https://stackoverflow.com/q/12259331/608639), etc. – jww Aug 19 '18 at 06:42
  • @jww OMG, how I hate SO, I regret every time I have to interact with this.... PLEASE **READ**, THIS IS **NOT** A DUPLICATE OF THOSE. I even referred to the dubbed "duplicate" in this damn question. – oblitum Sep 01 '18 at 16:38

2 Answers2

6

Use an array to store the extensions, then iterate over the array.

IFS=, read -a extensions <<< "$arg2"
for ext in "${extensions[@]}"; do
  for file in "$arg"/*."$ext"; do
      echo "$file" > z.txt
  done
done
chepner
  • 497,756
  • 71
  • 530
  • 681
  • 1
    Hmm. It's a fair question whether there's a case for leaving out the `-r` argument to `read` here; in favor of running without the `-r`, it allows extensions with commas to be used; against, it removes literal backslashes without any explicitly specified behavior that makes that desired. – Charles Duffy Jul 24 '18 at 20:26
  • 1
    Very gracious of you to assume I didn't just leave out the `-r` out of laziness :) (which I did). – chepner Jul 24 '18 at 20:27
  • The chance that anyone would want to have either comma or backslash in the extension is so close to zero that it has to be printed with exponential notation. – Barmar Jul 24 '18 at 20:28
  • @Barmar, ...I've got more outputs from `locate '*/*.*,*'` than you might expect. Granted, none of these look like files for which anyone would be searching by extension. – Charles Duffy Jul 24 '18 at 20:30
  • Thanks, great answer, I've accepted the other one because: https://stackoverflow.com/questions/51506835/for-loop-for-variable-multiple-extension#comment89983273_51507018 – oblitum Jul 24 '18 at 20:34
  • I like the other one as well. – chepner Jul 24 '18 at 20:36
  • @CharlesDuffy Right. Files like them exist (I vaguely recall a version-control system with `,version#` naming scheme), but the chance that you'd want to search for them in this type of script is negligible. – Barmar Jul 24 '18 at 20:39
  • *nod*, RCS and CVS used `,v` extensions, IIRC. (I actually have a long rant about that format, being former maintainer of a conversion tool from CVS repositories to the GNU Arch revision control system -- updates being prepended to the file, and UNIX not natively supporting prepend operations without a rewrite, it was easy to have old history become silently unavailable during updates, and to have this never be noticed until backups had aged out... so when someone was running my tool was often the first time they learned that a file suddenly no longer had pre-2004 history available). – Charles Duffy Jul 24 '18 at 20:41
6

You can use extglob to pass pipe delimited extension list like this:

arg2='jpg|jpeg|png'
shopt -s extglob nullglob

for file in "$arg"/*.@($arg2); do
    echo "$file"
done > z.txt

Or if you want to keep comma separated argument then use bash substitution to replace comma with pipe as this script:

arg2='jpg,jpeg,png'
shopt -s extglob nullglob

for file in "$arg"/*.@(${arg2//,/|}); do
    echo "$file"
done > z.txt
anubhava
  • 761,203
  • 64
  • 569
  • 643
  • 1
    Hmm. The original code would emit a literal output of `*.png` if no files matching that glob exist, whereas this will only emit the glob itself as output if no files matching **any** of the extensions given exist. Unless the OP ran `shopt -s nullglob` first, of course. – Charles Duffy Jul 24 '18 at 20:30
  • I was just adding `nullglob` while you commented this important point – anubhava Jul 24 '18 at 20:31
  • 1
    Thanks. I'm accepting this one because I can still change my argument syntax and replace commas for pipes, and it's shorter. – oblitum Jul 24 '18 at 20:33
  • 1
    If you still want to have comma separated argument then change `for file in "$arg"/*.@($arg2);` with `for file in "$arg"/*.@(${arg2//,/|});` – anubhava Jul 24 '18 at 20:35