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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11215088/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 22:19:49  来源:igfitidea点击:

bash shell script two variables in for loop

bashshellscriptingfor-loop

提问by snowmonkey

I am new to shell scripting. so kindly bear with me if my doubt is too silly.

我是 shell 脚本的新手。如果我的怀疑太愚蠢,请多多包涵。

I have png images in 2 different directories and an executable which takes an images from each directory and processes them to generate a new image.

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

I am looking for a for loop construct which can take two variables simultaneously..this is possible in C, C++ etc but how do I accomplish something of the following. The code is obviously wrong.

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

thanks!

谢谢!

回答by jordanm

If you are depending on the two directories to match up based on a locale sorted order (like your attempt), then an array should work.

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

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

回答by Paused until further notice.

Here are a few additional ways to do what you're looking for with notes about the pros and cons.

这里有一些额外的方法来做你正在寻找的关于利弊的注释。

The following only works with filenames that do not include newlines. It pairs the files in lockstep. It uses an extra file descriptor to read from the first list. If im1_dircontains more files, the loop will stop when im2_dirruns out. If im2_dircontains more files, file1will be empty for all unmatched file2. Of course if they contain the same number of files, there's no problem.

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

You can make the behavior consistent so that the loop stops with only non-empty matched files no matter which list is longer by replacing the semicolon with a double ampersand like so:

您可以通过将分号替换为双与号来使行为保持一致,以便循环仅以非空匹配文件停止,无论哪个列表更长,如下所示:

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

This version uses a forloop instead of a whileloop. This one stops when the shorter of the two lists run out.

此版本使用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

This version is similar to the one immediately above, but if one of the lists runs out it wraps around to reuse the items until the other one runs out. It's very ugly and you could do the same thing another way more simply.

此版本与上面的类似,但如果其中一个列表用完,它会回绕以重复使用这些项目,直到另一个列表用完为止。它非常丑陋,你可以用另一种更简单的方式做同样的事情。

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

This version only uses an array for the inner loop (second directory). It will only execute as many times as there are files in the first directory.

此版本仅将数组用于内部循环(第二个目录)。它只会执行与第一个目录中的文件一样多的次数。

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

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

回答by Hai Vu

If you don't mind going off the beaten path (bash), the Tool Command Language (TCL) has such a loop construct:

如果您不介意偏离常规 (bash),工具命令语言 (TCL) 具有这样的循环结构:

#!/usr/bin/env tclsh

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

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

Basically, the loop reads: for each item1 taken from list1, and item2 taken from list2. You can then replace command_namewith your own command.

基本上,循环读取:对于从 list1 中获取的每个 item1 和从 list2 中获取的 item2。然后你可以command_name用你自己的命令替换。

回答by Rashedul Islam

This might be another way to use two variables in the same loop. But you need to know the total number of files (or, the number of times you want to run the loop) in the directory to use it as the value of iteration i.

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

Get the number of files in the directory:

获取目录中的文件数:

ls /path/*.png | wc -l

Now run the loop:

现在运行循环:

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

For more help please see this discussion.

如需更多帮助,请参阅此讨论

回答by Thanh

It is very simple you can use two for loop functions in this problem.

很简单,您可以在此问题中使用两个 for 循环函数。

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

回答by John Haberstroh

I have this problem for a similar situation where I want a top and bottom range simultaneously. Here was my solution; it's not particularly efficient but it's easy and clean and not at all complicated with icky BASH arrays and all that nonsense.

对于我想要同时设置顶部和底部范围的类似情况,我遇到了这个问题。这是我的解决方案;它不是特别有效,但它很容易和干净,而且一点也不复杂,因为讨厌的 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

回答by Fritz G. Mehner

Another solution. The two lists with filenames are pasted into one.

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

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