2012-06-26 19 views
6

Sono nuovo allo script di shell. così gentilmente sopportami se il mio dubbio è troppo sciocco.script di shell bash due variabili per loop

Ho immagini png in 2 diverse directory e un eseguibile che prende le immagini da ciascuna directory e le elabora per generare una nuova immagine.

Sto cercando un costrutto di ciclo che può assumere due variabili simultaneamente. Questo è possibile in C, C++ ecc. Ma come faccio a realizzare qualcosa del seguente. Il codice è ovviamente sbagliato.

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

grazie!

+0

Stai cercando il "prodotto cartesiano"? Ad esempio, se la directory 1 ha i file A B C e la directory 2 ha i file a b c, stai cercando i 9 file di output (Aa, Ab, Ac, Ba, Bb, Bc, Ca, Cb, Cc) giusto? – jedwards

+1

@jedwards: Sto pensando che l'OP vuole Aa Bb Cc piuttosto che un prodotto cartesiano. –

+0

@DennisWilliamson: anche a me, ma pensarci troppo mi ha reso incerto – jedwards

risposta

8

Se si dipende dalle due directory in modo che corrispondano in base a un ordine ordinato delle impostazioni locali (come il proprio tentativo), allora un array dovrebbe funzionare.

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

Grazie. le immagini nelle due directory sono ordinate sì .. questo potrebbe funzionare ma ottengo un errore di sintassi ... "Errore di sintassi:" ("imprevisto" .... dovrebbero essere le parentesi tonde? e senza di essa "Errore di sintassi: Bad per variabile loop " – snowmonkey

+0

mi spiace intendo parentesi attorno a ~/prev1/*. Png – snowmonkey

+0

@ user1126374 - Questo perché/bin/sh non è bash. Modifica lo shebang in #!/Bin/bash – jordanm

3

Se non ti dispiace andare fuori dai sentieri battuti (bash), il Tool Command Language (TCL) ha una tale costrutto di ciclo:

#!/usr/bin/env tclsh 

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

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

In sostanza, il ciclo si legge: per ogni articolo1 preso da list1 e item2 preso da list2. È quindi possibile sostituire command_name con il proprio comando.

+0

puoi usare tclsh solo per una sezione di codice in un file src? guarderei.... –

3

Ecco alcuni altri modi per fare ciò che stai cercando con note su pro e contro.

Quanto segue funziona solo con nomi file che non includono il ritorno a capo. Si accoppia i file in un attimo. Usa un descrittore di file aggiuntivo per leggere dal primo elenco. Se im1_dir contiene più file, il ciclo si arresterà quando si esaurisce lo im2_dir. Se im2_dir contiene più file, file1 sarà vuoto per tutti gli impareggiabili file2. Naturalmente se contengono lo stesso numero di file, non ci sono problemi.

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

È possibile effettuare il comportamento coerente in modo che il ciclo si interrompe con i file solo non vuote abbinati indipendentemente da quale elenco è più lungo, sostituendo il punto e virgola con un doppio e commerciale in questo modo:

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

Questo versione utilizza un ciclo for anziché un ciclo while. Questo si ferma quando il più breve dei due elenchi si esaurisce.

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

Questa versione è simile a quella immediatamente superiore, ma se una delle liste si esaurisce si avvolge intorno a riutilizzare gli oggetti fino a quando l'altra esaurisce. È molto brutto e potresti fare la stessa cosa in un altro modo più semplicemente.

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

Questa versione utilizza solo una matrice per il ciclo interno (seconda directory). Verrà eseguito tutte le volte che ci sono file nella prima directory.

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

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

Un'altra soluzione. Le due liste con nomi di file sono incollate in una.

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

Questo potrebbe essere un altro modo di utilizzare due variabili nello stesso ciclo.Ma è necessario conoscere il numero totale di file (o il numero di volte in cui si desidera eseguire il ciclo) nella directory per utilizzarlo come valore di iterazione i.

ottenere il numero di file nella directory:

ls /path/*.png | wc -l 

Ora eseguire il ciclo:

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 

Per ulteriori informazioni consulta questo discussion.

0

È molto semplice utilizzare due funzioni di ciclo per questo problema.

bin bash

index = 0 per i in ~/INDIETRO1/.png
fare per j ~/PREV3/
.png fare run_black.sh $ i $ j fatto fatto

0

Ho questo problema per una situazione simile in cui voglio un intervallo superiore e inferiore simultaneamente. Ecco la mia soluzione; non è particolarmente efficiente, ma è facile e pulito e non è affatto complicato con gli array icky di BASH e tutte quelle assurdità.

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 
Problemi correlati