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

oyt4ldly  于 2023-05-18  发布在  Shell
关注(0)|答案(8)|浏览(295)

我是shell脚本的新手。如果我的怀疑太愚蠢请原谅我
我在2个不同的目录和一个可执行文件中的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

谢谢!

h79rfbju

h79rfbju1#

如果您依赖于两个目录来匹配基于区域设置排序的顺序(就像您的尝试),那么数组应该可以工作。

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
c90pui9n

c90pui9n2#

这里有一些额外的方法来做你正在寻找的关于利弊的笔记。
以下仅适用于不包含换行符的文件名。它会同步配对文件。它使用一个额外的文件描述符来读取第一个列表。如果im1_dir包含更多的文件,当im2_dir用完时,循环将停止。如果im2_dir包含更多文件,则对于所有不匹配的file2file1将为空。当然,如果它们包含相同数量的文件,就没有问题。

#!/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' "${im2_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
dzhpxtsq

dzhpxtsq3#

如果你不介意偏离常规路径(bash),工具命令语言(Tool Command Language,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

wlsrxk51

wlsrxk514#

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

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

a2mppw5e5#

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

dddzy1tm

dddzy1tm6#

我有这个问题的一个类似的情况下,我想要一个顶部和底部范围同时。这是我的解决办法;它不是特别有效,但它很简单、干净,对于讨厌的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
atmip9wb

atmip9wb7#

在这个问题中,你可以使用两个for循环函数。

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

h43kikqp8#

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

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

相关问题