Bash shell script two variables for the loop

I am new to shell scripting. Carry me so kindly if my doubts are too stupid.

I have png images in two different directories and an executable file that takes images from each directory and processes them to create a new image.

I am looking for a for loop construct that can take two variables at the same time. This is possible in C, C ++, etc., but how can I do one of the following. The code is clearly flawed.

#!/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!

+6
source share
7 answers

If you depend on two directories that match according to the ordered locale order (for example, your attempt), then the 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 
+8
source

If you don't mind going down the beaten path (bash), the tool command language (TCL) has this loop construction:

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

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

+3
source

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 
+3
source

This may be another way of using two variables in one loop. But you need to know the total number of files (or the number of times you want to run the loop) in the directory in order to use it as the iteration value i .

Get the number of files in a directory:

 ls /path/*.png | wc -l 

Now run the loop:

 im1_dir=(~/prev1/*.png) im2_dir=(~/prev3/*.png) for ((i = 0; i < 4; i++)); do run_black.sh ${im1_dir[i]} ${im2_dir[i]}; done 

See this discussion for more information.

0
source

Very simply, you can use two loop functions in this task.

bin bash

index = 0 for i in ~ / prev1 / .png
do for j ~ / prev3 / .png do run_black.sh $ i $ j done done

0
source

I have this problem for a similar situation where I need upper and lower ranges at the same time. Here is my solution; it is not particularly efficient, but it is simple and clean and not at all complicated with the help of icky BASH arrays and all this nonsense.

 SEQBOT=$(seq 0 5 $((PEAKTIME-5))) SEQTOP=$(seq 5 5 $((PEAKTIME-0))) IDXBOT=0 IDXTOP=0 for bot in $SEQBOT; do IDXTOP=0 for top in $SEQTOP; do if [ "$IDXBOT" -eq "$IDXTOP" ]; then echo $bot $top fi IDXTOP=$((IDXTOP + 1)) done IDXBOT=$((IDXBOT + 1)) done 
0
source

Another solution. Two lists with file names are inserted into one.

 paste <(ls --quote-name ~/prev1/*.png) <(ls --quote-name ~/prev3/*.png) | \ while read args ; do run_black $args done 
-1
source

Source: https://habr.com/ru/post/919069/


All Articles