Here are some additional ways to do what you are looking for with notes on the pros and cons.
The following only works with file names that do not include newlines. It binds files in a lock. It uses an additional file descriptor to read from the first list. If im1_dir
contains more files, the loop will end when im2_dir
ends. If im2_dir
contains more files, file1
will be empty for all unsurpassed file2
. Of course, if they contain the same number of files, there is no problem.
#!/bin/bash im1_dir=(~/prev1/*.png) im2_dir=(~/prev3/*.png) exec 3< <(printf '%s\n' "${im1_dir[@]}") while IFS=$'\n' read -r -u 3 file1; read -r file2 do run_black "$file1" "$file2" done < <(printf '%s\n' "${im1_dir[@]}") exec 3<&-
You can make the behavior consistent so that the loop stops only with non-empty matching files no matter which list is larger, replacing the semicolon with a double ampersand and so:
while IFS=$'\n' read -r -u 3 file1 && read -r file2
This version uses a for
loop instead of a while
. This stops when the shorter of the two lists ends.
#!/bin/bash im1_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 above, but if one of the lists ends, it wraps up to reuse items until the other ends. This is very ugly, and you can do the same in a different way more simply.
#!/bin/bash im1_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 uses only an array for the inner loop (second directory). It will be executed as many times as there are files in the first directory.
#!/bin/bash im1_dir=~/prev1/*.png im2_dir=(~/prev3/*.png) for file1 in $im1_dir do run_black "$file1" "${im2_dir[i++]}" done