bash shell脚本for循环中的两个变量

时间:2012-06-26 20:01:34

标签: bash shell scripting for-loop

我是shell脚本新手。如果我的怀疑太愚蠢,那么请耐心等待我。

我在两个不同的目录中有png图像和一个可执行文件,它从每个目录中获取图像并处理它们以生成新图像。

我正在寻找一个可以同时获取两个变量的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

谢谢!

8 个答案:

答案 0 :(得分:12)

如果您根据区域设置排序顺序(如尝试)依赖两个目录进行匹配,那么数组应该可以正常工作。

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

答案 1 :(得分:5)

以下是一些其他方法,可以通过有关利弊的说明来完成您所需的工作。

以下仅适用于不包含换行符的文件名。它以锁步方式配对文件。它使用额外的文件描述符从第一个列表中读取。如果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

答案 2 :(得分:4)

如果你不介意走出常规路径(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

答案 3 :(得分:2)

在这个问题中你可以使用两个for循环函数非常简单。

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

答案 4 :(得分:0)

这可能是在同一循环中使用两个变量的另一种方法。但是您需要知道目录中的文件总数(或者您希望运行循环的次数),以将其用作迭代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

如需更多帮助,请参阅此discussion

答案 5 :(得分:0)

我遇到类似情况的问题,我想同时处理顶部和底部范围。这是我的解决方案;它不是特别有效,但它既简单又干净,并且完全没有蹩脚的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

答案 6 :(得分:0)

使用 ${!array[@]} 语法迭代 array 的索引可以进一步简化接受的答案:

a=(x y z); b=(q w e); for i in ${!a[@]}; do echo ${a[i]}-${b[i]}; done

答案 7 :(得分:-1)

另一种解决方案。带有文件名的两个列表将粘贴到一个列表中。

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