параллельное выполнение команд bash - PullRequest
0 голосов
/ 29 января 2019

У меня есть скрипт bash, в котором есть цикл, внутри которого есть команда bash, которая вызывает другой скрипт bash, который, в свою очередь, вызывает скрипты python.

Каждая из этих команд bash внутри циклов может выполняться независимо друг от друга.Когда я позже запускаю его на реальном наборе данных, выполнение каждой команды занимает некоторое время.Поэтому я хотел бы воспользоваться и распараллелить эту часть сценария.

Я потратил несколько дней, изучая параметры в bash, которые выполняют параллельное выполнение, и в то же время давал мне возможность выбрать количество ядер, которые я хочу распараллелить, так, чтобы я не заполнил сервер.После поиска вариантов GNU, xargs -P показался мне наиболее разумным, поскольку мне не нужно иметь конкретную версию bash, и она будет работать без установки дополнительных библиотек.Однако я испытываю трудности с тем, чтобы заставить его работать, даже если это кажется прямым.

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

Вы можете игнорировать первые два вложенных цикла, я просто вставил весь сценарий для полноты.Часть, которая будет распараллелена, - это 4-я строка кода, считающая с конца скрипта:

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

Так что в основном это зациклит все пары, что в моем случае всего 1275 и в идеале будетвыполнить myFunction.sh параллельно с указанным числом потоков, используя переменную: $ n_threads

Однако я делаю что-то не так, потому что итератор k в этой строке не индексирует мои два массива $ {classes [k]}и $ {classes [k]} Цикл продолжает повторяться 1275 раз, но он только индексирует первый элемент обоих массивов, когда я их повторяю.Позже я изменил эту строку на эту для устранения неполадок:

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

он фактически увеличивает значение k при каждом цикле, однако, когда я изменяю эту строку на эту:

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

он печатает 0, 1275 раз как значение для k.Я не знаю, что я делаю неправильно, я почти уверен, что это должна быть какая-то синтаксическая ошибка.Любое руководство будет оценено.Большое спасибо в продвинутом.

ОБНОВЛЕНИЕ !!

На самом деле у меня есть два вектора, которые имеют одинаковые размеры и вводятся для сценария myFunction.sh.Я просто хочу, чтобы целочисленный индекс мог одновременно идентифицировать их и вызывать мою функцию с этими двумя значениями, которые индексируются из этих двух векторов.Я изменил свой код следующим образом, основываясь на вашем предложении:

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

, однако теперь, когда я выполняю код, я получаю следующую ошибку:

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

Я думаю, этот индекс '@@'все еще в строковом формате.Я просто хочу, чтобы целочисленные индикаторы генерировались во время цикла и я мог выполнять эту команду параллельно.

Ответы [ 3 ]

0 голосов
/ 29 января 2019

Эта строка не работает так, как вы думаете:

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

В результате BASH сначала расширит такие вещи, как $n_threads и ${classes[k]}, в строки, а затем вызовет xargs.Btw.${classes[k]} всегда "", поскольку ключ "k" отсутствует в массиве classes.Попробуйте ${classes[$k]};затем BASH сначала подставит переменную k, а затем использует результат для поиска значения в classes.

Возможно, лучшим подходом было бы записать значения из classes в файл и использовать этов качестве ввода для xargs.Возможно, вам придется изменить myFunction.sh, чтобы принять один аргумент (= одну строку ввода) и разобрать его в сценарии.

0 голосов
/ 30 января 2019

С GNU Parallel вы, вероятно, можете сделать:

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

или:

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

Тогда вы можете пропустить все поколение tree_match.txt и $ col1 / $ col2.

Используйте parallel --embed, чтобы включить GNU Parallel непосредственно в ваш скрипт, чтобы у вас не было внешних зависимостей.

0 голосов
/ 29 января 2019

Для рассматриваемой строки:

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

${classes[k]} будет расширен оболочкой (скорее всего, ничего), прежде чем xargs сможет ее увидеть.

ВозможноВы можете изменить порядок на:

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