我是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
谢谢!
答案 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