Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

bash shell script two variables in for loop

I am new to shell scripting. so kindly bear with me if my doubt is too silly.

I have png images in 2 different directories and an executable which takes an images from each directory and processes them to generate a new image.

I am looking for a for loop construct which can take two variables simultaneously..this is possible in C, C++ etc but how do I accomplish something of the following. The code is obviously wrong.

#!/bin/sh

im1_dir=~/prev1/*.png  
im2_dir=~/prev3/*.png
index=0

for i,j in $im1_dir $im2_dir  # i iterates in im1_dir and j iterates in im2_dir 
do
  run_black.sh $i $j  
done

thanks!

like image 686
snowmonkey Avatar asked Jun 26 '12 20:06

snowmonkey


People also ask

Can we use two variables in for loop?

Yes, I can declare multiple variables in a for-loop. And you, too, can now declare multiple variables, in a for-loop, as follows: Just separate the multiple variables in the initialization statement with commas. Do not forget to end the complete initialization statement with a semicolon.

What is && and || in shell script?

The operators "&&" and "||" shall have equal precedence and shall be evaluated with left associativity. For example, both of the following commands write solely bar to standard output: $ false && echo foo || echo bar $ true || echo foo && echo bar.

How do you add multiple variables to a loop?

In Java, multiple variables can be initialized in the initialization block of for loop regardless of whether you use it in the loop or not.


2 Answers

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/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 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/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 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/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 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/bash
im1_dir=~/prev1/*.png
im2_dir=(~/prev3/*.png)

for file1 in $im1_dir
do
    run_black "$file1" "${im2_dir[i++]}"
done
like image 162
Dennis Williamson Avatar answered Sep 28 '22 03:09

Dennis Williamson


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
like image 32
jordanm Avatar answered Sep 28 '22 02:09

jordanm