it-roy-ru.com

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

Я новичок в сценариях Shell. пожалуйста, потерпите меня, если мои сомнения слишком глупы. 

У меня есть 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

спасибо!

10
snowmonkey

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

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
12
jordanm

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

Следующее работает только с именами файлов, которые не включают переводы строки. Он объединяет файлы в стопку. Он использует дополнительный файловый дескриптор для чтения из первого списка. Если 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
5
Dennis Williamson

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

#!/usr/bin/env tclsh

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

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

По сути, цикл гласит: для каждого элемента 1, взятого из списка 1, и элемента 2, взятого из списка 2 . Затем вы можете заменить command_name своей собственной командой.

4
Hai Vu

В этой задаче очень просто использовать две функции цикла.

#bin bash
index=0
for i in ~/prev1/*.png  
do
    for j ~/prev3/*.png
    do 
        run_black.sh $i $j
    done
done
2
Thanh

Это может быть другой способ использовать две переменные в одном цикле. Но вам нужно знать общее количество файлов (или количество раз, которое вы хотите запустить цикл) в каталоге, чтобы использовать его в качестве значения итерации 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
Rashedul Islam

У меня есть эта проблема для аналогичной ситуации, когда я хочу верхний и нижний диапазон одновременно. Здесь было мое решение; это не особенно эффективно, но легко и чисто, и совсем не сложно с массивами icky 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
0
John Haberstroh