bash shell script two variables in for loop bash shell script two variables in for loop shell shell

bash shell script two variables in for loop


If you are depending on the two directories to match up based on a locale sorted order (like your attempt), then an array should work.

im1_files=(~/prev1/*.png)im2_files=(~/prev3/*.png)for ((i=0;i<=${#im1_files[@]};i++)); do   run_black.sh "${im1_files[i]}" "${im2_files[i]}"done


Here are a few additional ways to do what you're looking for with notes about the pros and cons.

The following only works with filenames that do not include newlines. It pairs the files in lockstep. It uses an extra file descriptor to read from the first list. If im1_dir contains more files, the loop will stop when im2_dir runs out. If im2_dir contains more files, file1 will be empty for all unmatched file2. Of course if they contain the same number of files, there's no problem.

#!/bin/bashim1_dir=(~/prev1/*.png) im2_dir=(~/prev3/*.png)exec 3< <(printf '%s\n' "${im1_dir[@]}")while IFS=$'\n' read -r -u 3 file1; read -r file2do    run_black "$file1" "$file2"done < <(printf '%s\n' "${im1_dir[@]}")exec 3<&-

You can make the behavior consistent so that the loop stops with only non-empty matched files no matter which list is longer by replacing the semicolon with a double ampersand like so:

while IFS=$'\n' read -r -u 3 file1 && read -r file2

This version uses a for loop instead of a while loop. This one stops when the shorter of the two lists run out.

#!/bin/bashim1_dir=(~/prev1/*.png) im2_dir=(~/prev3/*.png)for ((i = 0; i < ${#im1_dir[@]} && i < ${#im2_dir[@]}; i++))do    run_black "${im1_dir[i]}" "${im2_dir[i]}"done

This version is similar to the one immediately above, but if one of the lists runs out it wraps around to reuse the items until the other one runs out. It's very ugly and you could do the same thing another way more simply.

#!/bin/bashim1_dir=(~/prev1/*.png) im2_dir=(~/prev3/*.png)for ((i = 0, j = 0,          n1 = ${#im1_dir[@]},           n2 = ${#im2_dir[@]},           s = n1 >= n2 ? n1 : n2,           is = 0, js = 0;       is < s && js < s;       i++, is = i, i %= n1,           j++, js = j, j %= n2))do    run_black "${im1_dir[i]}" "${im2_dir[i]}"done

This version only uses an array for the inner loop (second directory). It will only execute as many times as there are files in the first directory.

#!/bin/bashim1_dir=~/prev1/*.pngim2_dir=(~/prev3/*.png)for file1 in $im1_dirdo    run_black "$file1" "${im2_dir[i++]}"done


If you don't mind going off the beaten path (bash), the Tool Command Language (TCL) has such a loop construct:

#!/usr/bin/env tclshset list1 [glob dir1/*]set list2 [glob dir2/*]foreach item1 $list1 item2 $list2 {    exec command_name $item1 $item2}

Basically, the loop reads: for each item1 taken from list1, and item2 taken from list2. You can then replace command_name with your own command.