web-dev-qa-db-fra.com

script bash shell deux variables dans la boucle

Je suis nouveau dans les scripts Shell. si gentiment supporter avec moi si mon doute est trop bête. 

J'ai des images png dans 2 répertoires différents et un exécutable qui prend une image de chaque répertoire et les traite pour générer une nouvelle image. 

Je suis à la recherche d'une construction de boucle for pouvant prendre deux variables simultanément. Ceci est possible en C, C++, etc. Le code est évidemment faux. 

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

merci!

10
snowmonkey

Si vous comptez sur les deux répertoires à mettre en correspondance en fonction d'un ordre de tri des paramètres régionaux (comme votre tentative), un tableau devrait alors fonctionner.

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
12
jordanm

Voici quelques moyens supplémentaires de faire ce que vous cherchez avec des notes sur les avantages et les inconvénients.

Ce qui suit ne fonctionne qu'avec les noms de fichiers qui n'incluent pas de nouvelles lignes. Il associe les fichiers en un rien de temps. Il utilise un descripteur de fichier supplémentaire pour lire à partir de la première liste. Si im1_dir contient plus de fichiers, la boucle s'arrêtera lorsque im2_dir sera épuisé. Si im2_dir contient plus de fichiers, file1 sera vide pour tous les file2 sans correspondance. Bien sûr, s'ils contiennent le même nombre de fichiers, il n'y a pas de problème.

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

Vous pouvez rendre le comportement cohérent afin que la boucle s'arrête avec uniquement les fichiers correspondants non vides, quelle que soit la liste la plus longue, en remplaçant le point-virgule par un double et commercial, comme suit:

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

Cette version utilise une boucle for au lieu d'une boucle while. Celui-ci s'arrête lorsque la plus courte des deux listes s'épuise.

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

Cette version est similaire à celle ci-dessus, mais si l'une des listes est épuisée, elle est renvoyée pour permettre de réutiliser les éléments jusqu'à ce que l'autre soit épuisée. C'est très moche et vous pourriez faire la même chose d'une autre manière plus simplement.

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

Cette version utilise uniquement un tableau pour la boucle interne (deuxième répertoire). Il ne s'exécutera autant de fois qu'il y a de fichiers dans le premier répertoire.

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

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

Si cela ne vous dérange pas de sortir des sentiers battus (bash), le langage de commande d'outils (TCL) possède une telle construction de boucle:

#!/usr/bin/env tclsh

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

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

Fondamentalement, la boucle se lit comme suit: pour chaque élément1 extrait de liste1 et élément2 extrait de liste2 . Vous pouvez alors remplacer command_name par votre propre commande.

4
Hai Vu

C'est très simple, vous pouvez utiliser deux fonctions de boucle for dans ce problème.

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

Cela pourrait être un autre moyen d'utiliser deux variables dans la même boucle. Mais vous devez connaître le nombre total de fichiers (ou le nombre de fois que vous souhaitez exécuter la boucle) dans le répertoire pour pouvoir l'utiliser comme valeur de l'itération i

Obtenir le nombre de fichiers dans le répertoire:

ls /path/*.png | wc -l

Maintenant, lancez la boucle:

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

Pour plus d'aide s'il vous plaît voir cette discussion .

0
Rashedul Islam

J'ai ce problème pour une situation similaire où je veux un haut et un bas en même temps. Voici ma solution. ce n'est pas particulièrement efficace mais c'est facile et propre et pas du tout compliqué avec les tableaux noires de BASH et tout ce non-sens.

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
0
John Haberstroh