2012-06-26 14 views
6

Ich bin neu im Shell-Scripting. so bitte ertragen Sie mich, wenn mein Zweifel zu albern ist.bash shell script zwei Variablen in for loop

Ich habe png Bilder in 2 verschiedenen Verzeichnissen und eine ausführbare Datei, die ein Bild aus jedem Verzeichnis nimmt und verarbeitet, um ein neues Bild zu erzeugen.

Ich bin auf der Suche nach einem for-Schleife-Konstrukt, das zwei Variablen gleichzeitig nehmen kann. Dies ist in C, C++ usw. möglich, aber wie erreiche ich etwas vom Folgenden. Der Code ist offensichtlich falsch.

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

danke!

+0

Suchen Sie das "kartesische Produkt"? Zum Beispiel, wenn Verzeichnis 1 Dateien A B C und Verzeichnis 2 hat Dateien a b c, Sie suchen nach den 9 Ausgabedateien (Aa, Ab, Ac, Ba, Bb, Bc, Ca, Cb, Cc) richtig? – jedwards

+1

@jedwards: Ich denke, das OP will Aa Bb Cc anstatt ein kartesisches Produkt. –

+0

@DennisWilliamson: ich auch, aber zu viel darüber nachzudenken machte mich unsicher – jedwards

Antwort

8

Wenn Sie von den zwei Verzeichnissen abhängig sind, die auf der Grundlage einer nach Gebietsschema sortierten Reihenfolge abgeglichen werden (wie bei Ihrem Versuch), sollte ein Array funktionieren.

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

Danke. die Bilder in den beiden Verzeichnissen sind ja sortiert .. das könnte funktionieren, aber ich bekomme einen Syntaxfehler ... "Syntaxfehler:" ("Unerwartetes" .... sollten die runden Klammern da sein? und ohne "Syntaxfehler: Bad for loop Variable " – snowmonkey

+0

sorry ich meine Klammern um ~/prev1/*. Png – snowmonkey

+0

@ user1126374 - Das ist, weil Ihre/bin/sh ist nicht Bash. Ändern Sie die shebang zu #!/Bin/bash – jordanm

3

Wenn Sie nicht die ausgetretenen Pfade abgehend dagegen (bash), das Tool Command Language (TCL) hat eine solche Schleife Konstrukt:

#!/usr/bin/env tclsh 

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

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

Grundsätzlich ist die Schleife lautet: für jedes Element1 aus Liste1 und Element2 aus Liste2. Sie können dann command_name durch Ihren eigenen Befehl ersetzen.

+0

können Sie Tclsh nur für einen Abschnitt des Codes in einer Src-Datei verwenden? Ich frage mich, wie das würde aussehen.... –

3

Hier sind ein paar zusätzliche Möglichkeiten, was Sie suchen mit Notizen über die Vor- und Nachteile.

Folgendes funktioniert nur mit Dateinamen, die keine Zeilenumbrüche enthalten. Es paart die Dateien im Lockstep. Es verwendet einen zusätzlichen Dateideskriptor, um von der ersten Liste zu lesen. Wenn im1_dir mehr Dateien enthält, stoppt die Schleife, wenn im2_dir abläuft. Wenn im2_dir mehr Dateien enthält, ist file1 für alle unerreichten file2 leer. Natürlich, wenn sie die gleiche Anzahl von Dateien enthalten, gibt es kein Problem.

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

Sie konsequent das Verhalten machen kann, so dass die Schleife, egal mit nur nicht leer abgestimmt Dateien stoppt die Liste länger ist wie so das Semikolon mit einem Doppelampersand durch Ersetzen:

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

Diese Version verwendet eine for Schleife anstelle einer while Schleife. Dieser stoppt, wenn die kürzere der beiden Listen ausgeht.

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

Diese Version ist ähnlich dem unmittelbar über, aber wenn einer der Listen abläuft es wickelt sich um die Elemente wieder zu verwenden, bis der andere abläuft. Es ist sehr hässlich und du könntest dasselbe auf eine andere Art und Weise einfacher machen.

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

Diese Version verwendet nur ein Array für die innere Schleife (zweites Verzeichnis). Es wird nur so oft ausgeführt, wie Dateien im ersten Verzeichnis vorhanden sind.

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

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

Eine andere Lösung. Die zwei Listen mit Dateinamen werden in einen eingefügt.

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

Dies könnte eine andere Möglichkeit sein, zwei Variablen in der gleichen Schleife zu verwenden.Sie müssen jedoch die Gesamtanzahl der Dateien (oder die Häufigkeit, mit der die Schleife ausgeführt werden soll) im Verzeichnis kennen, um sie als Wert für die Iteration i zu verwenden.

die Anzahl der Dateien im Verzeichnis Get:

ls /path/*.png | wc -l 

nun die Schleife laufen:

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 

Weitere Hilfe diese discussion bitte.

0

Es ist sehr einfach, Sie können zwei For-Schleife-Funktionen in diesem Problem verwenden.

bin bash

index = 0 für i in ~/VORH1/.png
tun für j ~/PREV3/
.png run_black.sh $ i $ j tun done done

0

Ich habe dieses Problem für eine ähnliche Situation, wo ich einen oberen und unteren Bereich gleichzeitig wollen. Hier war meine Lösung; es ist nicht besonders effizient, aber es ist einfach und sauber und überhaupt nicht kompliziert mit ekligen BASH-Arrays und all dem Unsinn.

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