1
votes

Exécution parallèle des commandes Bash

J'ai un script Bash qui a une boucle à l'intérieur de laquelle il y a une commande Bash qui appelle un autre script Bash qui à son tour appelle des scripts Python.

Chacune de ces commandes bash dans les boucles pourrait être exécutée indépendamment l'une de l'autre . Lorsque je l'exécute plus tard sur un ensemble de données réel, l'exécution de chaque commande prend un certain temps. Par conséquent, je voudrais profiter et paralléliser cette partie du script.

J'ai passé quelques jours à passer en revue les options dans Bash qui font une exécution parallèle, tout en me donnant également la possibilité de choisir le nombre de cœurs que je veux paralléliser le code de manière à ne pas inonder le serveur. Après avoir recherché les options GNU, xargs -P m'a semblé la plus raisonnable, car je n'ai pas besoin d'avoir une version spécifique de Bash et cela fonctionnera sans installer de bibliothèques supplémentaires. Cependant, j'ai des difficultés à le faire fonctionner, même si cela semble simple.

@@: syntax error: operand expected (error token is "@@")

Vous pouvez ignorer les deux premières boucles imbriquées, j'ai juste collé le script entier pour être complet. La partie qui va être parallélisée est la 4ème ligne de code à compter de la fin du script:

 for x in {0..10};
    do
        printf "%d\0" "$x"; done| xargs -0 -I @@ -P $n_threads sh markerGenes2TreeMatch.sh -1 ${col1[@@]}-2 ${col2[@@]}

Cela bouclera sur toutes les paires ce qui est dans mon cas 1275 in total et exécutera idéalement myFunction.sh en parallèle avec le nombre spécifié de threads en utilisant la variable $n_threads .

Cependant, je fais quelque chose de mal car l'itérateur k dans cette ligne n'indexe pas mes deux tableaux $ {classes [k]} et $ {classes [k]} .

La boucle continue d'itérer 1275 fois mais elle n'indexe que le premier élément des deux tableaux lorsque je les répète. J'ai plus tard changé cette ligne en ceci pour le dépannage:

printf "%s\0" {0..1275} | xargs -0 -I k -P $n_threads echo "index" "$((k))"

Il incrémente en fait la valeur de k à chaque fois qu'il boucle, cependant quand je change cela ligne à ceci:

printf "%s\0" {0..1275} | xargs -0 -I k -P $n_threads echo "index" k

il imprime 0, 1275 fois comme valeur pour k . Je ne sais pas ce que je fais de mal.

J'ai en fait deux vecteurs qui sont de la même taille et sont saisis pour le script myFunction.sh . Je veux juste qu'un index entier puisse les indexer en même temps et appeler ma fonction avec ces deux valeurs indexées à partir de ces deux vecteurs. J'ai modifié mon code comme suit en fonction de votre suggestion:

printf "%s\0" {0..1275} | xargs -0 -I k -P $n_threads sh myFunction.sh -1 ${classes[k]} -2 ${classes[k]}

mais maintenant, lorsque j'exécute le code, j'obtiens l'erreur suivante:

#!/bin/bash

while getopts i:t: option
do
case "${option}"
in
    i) in_f=${OPTARG};;
    t) n_threads=${OPTARG};;
esac
done    

START=$(date +%s)
class_file=$in_f
classes=( $(awk '{print $1}' ./$class_file))
rm -r tree_matches.txt
n="${#classes[@]}"
for i in $(seq 0  $n);
   do
     for j in $(seq $((i+1)) $((n-1)));
         do
            echo ${classes[i]}"    "${classes[j]} >> tree_matches.txt
         done
   done
col1=( $(awk '{print $1}' ./tree_matches.txt ))
col2=( $(awk '{print $2}' ./tree_matches.txt ))


printf "%s\0" {0..1275} | xargs -0 -I k -P $n_threads sh myFunction.sh -1 ${classes[k]} -2 ${classes[k]}

n_pairs="${#col1[@]}"

END=$(date +%s)
DIFF=$(( $END - $START ))
echo "Exec time $DIFF seconds"


3 commentaires

Je ne sais pas pourquoi votre dernière commande ne fonctionne pas, mais notez que -I implique -L 1 , c'est-à-dire qu'une seule ligne si l'entrée sera traitée à un temps.


k ne peut être incrémenté de xargs que s'il le voit! $ {classes [k]} est développé par le script initial


Oui, je pense que le problème est l'ordre d'évaluation. $ ((...)) est géré par le shell avant que xargs ne puisse le voir, c'est pourquoi $ (({})) (ou tout ce que vous utilisez comme argument de -I ) ne fonctionne pas. Vous devrez peut-être utiliser bash -c dans votre commande xargs , voir le manuel .


3 Réponses :


1
votes

Pour la ligne en question:

for x in {0..1275}; do printf "%s\0" "${classes[$x]}"; done |\
xargs -0 -I @@ -P $n_threads sh myFunction.sh -1 @@ -2 @@

$ {classes [k]} sera développé par le shell (à rien de très probable), avant que xargs n'ait une chance de le voir.

Vous pourriez peut-être le réorganiser en:

printf "%s\0" {0..1275} | xargs -0 -I k -P $n_threads sh myFunction.sh -1 ${classes[k]} -2 ${classes[k]}


4 commentaires

J'obtiens à nouveau la même erreur, en fait, j'ai deux vecteurs qui sont entrés dans myFunction.sh ces deux vecteurs sont de la même taille et je peux utiliser une valeur d'index par exécution de commande pour appeler cette fonction


vous pouvez remplacer sh par echo et vérifier que la valeur attendue est transmise. Êtes-vous sûr que les classes contiennent des données valides?


c'est en fait deux vecteurs, col1 et col2, j'ai mis à jour mon article principal pour clarifier cela.


ok je l'ai finalement compris ... J'ai simplifié le script myFunction.sh de telle sorte qu'il prend maintenant un argument, puis il divise (coupe) la ligne en deux en utilisant une virgule comme délimiteur. J'ai suivi votre structure suggérée et son bon fonctionnement. Merci à tous pour les suggestions.



0
votes

Cette ligne ne fonctionne pas comme vous le pensez:

printf "%s\0" {0..1275} | xargs -0 -I k -P $n_threads sh myFunction.sh -1 ${classes[k]} -2 ${classes[k]}

Ce qui se passe, c'est que BASH va d'abord développer des éléments tels que $ n_threads et $ {classe [k]} en chaînes, puis appelle xargs . Btw. $ {classes [k]} est toujours "" car la clé "k" n'est pas dans le tableau classes code>. Essayez $ {classes [$ k]} ; puis BASH remplacera d'abord la variable k , puis utilisera le résultat pour rechercher une valeur dans classes .

Peut-être qu'une meilleure approche serait d'écrire le valeurs de classes dans un fichier et utilisez-le comme entrée pour xargs . Vous devrez peut-être changer myFunction.sh pour accepter un seul argument (= une ligne d'entrée) et le démonter dans le script.


0 commentaires

0
votes

Avec GNU Parallel, vous pourriez probablement faire:

parallel --plus markerGenes2TreeMatch.sh -1 {1choose_k} -2 {2choose_k} ::: ${classes[@]} ::: ${classes[@]}

ou:

classes=( $(awk '{print $1}' ./$class_file))
parallel markerGenes2TreeMatch.sh -1 {=1 'if($arg[1] ge $arg[2]) { skip() }' =} -2 {2} ::: ${classes[@]} ::: ${classes[@]}

Ensuite, vous pouvez ignorer toute la génération de tree_match.txt , et $ col1 / $ col2.

Utilisez parallel --embed pour inclure GNU Parallel directement dans votre script, vous n'avez donc pas de dépendances externes.

p>


0 commentaires