сценарий оболочки bash две переменные в цикле for

Я новичок в скриптах оболочки. будь добр, потерпи меня, если мои сомнения слишком глупы.

У меня есть png-изображения в 2 разных каталогах и исполняемый файл, который берет изображения из каждого каталога и обрабатывает их для создания нового изображения.

Я ищу конструкцию цикла for, которая может принимать две переменные одновременно..это возможно в C, C++ и т. д., Но как я могу выполнить что-то из следующего. Очевидно, что код неверен.

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

спасибо!

7 ответов


Если вы зависите от двух каталогов, чтобы соответствовать на основе порядка сортировки локали (например, ваша попытка), то массив должен работать.

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

Если вы не против сойти с проторенного пути (bash), командный язык инструмента (TCL) имеет такую конструкцию цикла:

#!/usr/bin/env tclsh

set list1 [glob dir1/*]
set list2 [glob dir2/*]

foreach item1 $list1 item2 $list2 {
    exec command_name $item1 $item2
}

в основном, цикл читает:для каждого item1 взятого из list1, и item2 взятого из list2. Затем вы можете заменить command_name С вашей собственной командой.


вот несколько дополнительных способов сделать то, что вы ищете с заметками о плюсах и минусах.

далее работает только с именами файлов, которые не содержат строки. Он соединяет файлы в lockstep. Он использует дополнительный файловый дескриптор для чтения из первого списка. Если im1_dir содержит больше файлов, цикл остановится, когда im2_dir закончится. Если im2_dir содержит больше файлов, file1 будет пустым для всех непревзойденных file2. Конечно, если они содержат одинаковое количество файлы, нет проблем.

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

вы можете сделать поведение согласованным, чтобы цикл останавливался только с непустыми совпадающими файлами независимо от того, какой список длиннее, заменив точку с запятой двойным амперсандом, например:

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

эта версия использует for петли вместо while петли. Этот останавливается, когда заканчивается более короткий из двух списков.

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

эта версия аналогична приведенной выше, но если один из списков заканчивается он оборачивается, чтобы повторно использовать элементы, пока не закончится другой. Это очень уродливо, и вы могли бы сделать то же самое по-другому, проще.

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

эта версия использует только массив для внутреннего цикла (второй каталог). Он будет выполняться только столько раз, сколько есть файлов в первом каталоге.

#!/bin/bash
im1_dir=~/prev1/*.png
im2_dir=(~/prev3/*.png)

for file1 in $im1_dir
do
    run_black "$file1" "${im2_dir[i++]}"
done

это может быть другой способ использовать две переменные в одном цикле. Но вам нужно знать общее количество файлов (или количество раз, когда вы хотите запустить цикл) в каталоге, чтобы использовать его в качестве значения итерации i.

получить количество файлов в папке:

ls /path/*.png | wc -l

Теперь запустите цикл:

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

для получения дополнительной помощи см. Это обсуждение.


Это очень просто, вы можете использовать два для функций цикла в этой проблеме.

бин Баш

=0 для я в ~/prev1/.формат PNG
делать для J в ~/prev3/
.формат PNG делать run_black.sh $i $j сделанный сделал

У меня есть эта проблема для аналогичной ситуации, когда я хочу верхний и нижний диапазон одновременно. Вот мое решение; это не особенно эффективно, но это легко и чисто и совсем не сложно с грязными массивами BASH и всей этой ерундой.

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

другое решение. Два списка с именами файлов вставляются в один.

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