2012-06-26 104 views
6

我是shell腳本編程新手。如果我的懷疑太愚蠢,那麼我會很友善。bash shell腳本for循環中的兩個變量

我有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 

謝謝!

+0

您是否在尋找「笛卡爾產品」?例如,如果目錄1包含文件A B C,而目錄2包含文件a b c,那麼您正在尋找9個輸出文件(Aa,Ab,Ac,Ba,Bb,Bc,Ca,Cb,Cc)嗎? – jedwards

+1

@jedwards:我在想OP是想要Aa Bb Cc而不是笛卡爾產品。 –

+0

@丹尼斯威廉姆森:我也是,但考慮太多讓我不確定 – jedwards

回答

8

如果你根據兩個目錄來匹配基於區域排序順序(如你的嘗試),那麼一個數組應該工作。

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 
+0

謝謝。在這兩個目錄中的圖像被排序是..這可以工作,但我得到一個語法錯誤......「語法錯誤:」(「意外」....應該在那裏圓括號?沒有它「語法錯誤:不好的循環變量「 – snowmonkey

+0

對不起,我的意思是括號內〜/ prev1/*。png – snowmonkey

+0

@ user1126374 - 那是因爲你的/ bin/sh不是bash。將shebang改成#!/ bin/bash – jordanm

3

如果你不介意因循守舊(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

+0

你可以使用tclsh作爲src文件中的一段代碼嗎?我想知道這是怎麼回事會看起來.... –

3

這裏有一些額外的方式來做你要找的利弊注意事項。

以下僅適用於不包含換行符的文件名。它以鎖步方式將文件配對。它使用額外的文件描述符從第一個列表中讀取。如果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 
-1

另一種解決方案。這兩個帶有文件名的列表被粘貼成一個。

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

0

這很簡單,你可以在這個問題中使用兩個循環函數。

倉的bash

索引= 0 爲 我在〜/ prev1/png格式
爲 j執行 〜/ PREV3/
png格式 做 run_black.sh $ I $Ĵ 完成 完成

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