Иметь работу только после завершения всех моих предыдущих работ
Я нашел сообщение, показывающее, как я мог бы сказать bsub ждать завершения заданного набора заданий, прежде чем запускать здесь, однако это работает, только если известно количество заданий перед этим.
Я хотел бы выполнить произвольное количество заданий и выполнить "завершение" задания после того, как все мои задания будут завершены
вот мой сценарий:
#!/bin/bash
for file in dir/*; do # I don't know how many jobs will be created
bsub "./do_it_once.sh $file"
done
bsub -w "done(1) && done(2) && done(3)" merge_results.sh
Приведенный выше сценарий будет работать, когда отправлено 3 задания, но что, если имеется n заданий? Как я могу указать, что я хочу дождаться завершения всех заданий?
3 ответа
Исходя из ответа cxw, я получил кое-что работающее. Он не использует массивы. Однако команда -w может принимать подстановочные знаки, поэтому я назвал каждое задание одинаково.
Все еще не уверен, что это правильный способ звонка bsub
, так как вам нужно вызывать его один раз каждый раз, но это работает.
отредактировано из cxw:
#!/bin/bash
jobnum=0
for file in src/*; do # I don't know how many jobs will be created
bsub -J "myjobs${jobnum}" "./do_it_once.sh $file"
jobnum=$((jobnum + 1))
done
bsub -w "done(myjobs*)" merge_results.sh
Редактировать Смотрите ответ Камулы о том, что на самом деле работает:) .
Оригинальный ответ
Никогда не использовался bsub
, но из быстрого ознакомления с man-страницей, я думаю, что это может сделать это:
#!/bin/bash
jobnum=0
for file in src/*; do # I don't know how many jobs will be created
bsub -J "myjobs[$jobnum]" "./do_it_once.sh $file"
jobnum=$((jobnum + 1))
done
bsub -w "done(myjobs[*])" merge_results.sh
Рабочие места названы с последовательными индексами в bsub
массив называется myjobs[]
, с помощью bash
переменная jobnum
, Тогда последний bsub
ждет всех myjobs[]
работы до конца.
YMMV!
О - также, возможно, вам придется использовать -J "\"myjobs[...]\""
(с \"
). На странице руководства написано, что имена рабочих мест заключаются в двойные кавычки, но я не знаю, bsub
требование или если они предполагают, что вы будете использовать оболочку, которая расширяет текст без кавычек.
Вот мое полное решение, которое добавляет контроль времени и дает количество неудачных заданий. Также заботится о том, чтобы убивать детей, потерявших работу, если это необходимо, и занимается зомби или бесперебойными процессами:
function Logger {
echo "$1"
}
# Portable child (and grandchild) kill function tester under Linux, BSD and MacOS X
function KillChilds {
local pid="${1}" # Parent pid to kill childs
local self="${2:-false}" # Should parent be killed too ?
if children="$(pgrep -P "$pid")"; then
KillChilds "$child" true
done
fi
# Try to kill nicely, if not, wait 15 seconds to let Trap actions happen before killing
if ( [ "$self" == true ] && kill -0 $pid > /dev/null 2>&1); then
kill -s TERM "$pid"
if [ $? != 0 ]; then
sleep 15
Logger "Sending SIGTERM to process [$pid] failed."
kill -9 "$pid"
if [ $? != 0 ]; then
Logger "Sending SIGKILL to process [$pid] failed."
return 1
fi
else
return 0
fi
else
return 0
fi
}
function WaitForTaskCompletion {
local pids="${1}" # pids to wait for, separated by semi-colon
local soft_max_time="${2}" # If program with pid $pid takes longer than $soft_max_time seconds, will log a warning, unless $soft_max_time equals 0.
local hard_max_time="${3}" # If program with pid $pid takes longer than $hard_max_time seconds, will stop execution, unless $hard_max_time equals 0.
local caller_name="${4}" # Who called this function
local counting="${5:-true}" # Count time since function has been launched if true, since script has been launched if false
local keep_logging="${6:-0}" # Log a standby message every X seconds. Set to zero to disable logging
local soft_alert=false # Does a soft alert need to be triggered, if yes, send an alert once
local log_ttime=0 # local time instance for comparaison
local seconds_begin=$SECONDS # Seconds since the beginning of the script
local exec_time=0 # Seconds since the beginning of this function
local retval=0 # return value of monitored pid process
local errorcount=0 # Number of pids that finished with errors
local pid # Current pid working on
local pidCount # number of given pids
local pidState # State of the process
local pidsArray # Array of currently running pids
local newPidsArray # New array of currently running pids
IFS=';' read -a pidsArray <<< "$pids"
pidCount=${#pidsArray[@]}
WAIT_FOR_TASK_COMPLETION=""
while [ ${#pidsArray[@]} -gt 0 ]; do
newPidsArray=()
Spinner
if [ $counting == true ]; then
exec_time=$(($SECONDS - $seconds_begin))
else
exec_time=$SECONDS
fi
if [ $keep_logging -ne 0 ]; then
if [ $((($exec_time + 1) % $keep_logging)) -eq 0 ]; then
if [ $log_ttime -ne $exec_time ]; then # Fix when sleep time lower than 1s
log_ttime=$exec_time
fi
fi
fi
if [ $exec_time -gt $soft_max_time ]; then
if [ $soft_alert == true ] && [ $soft_max_time -ne 0 ]; then
Logger "Max soft execution time exceeded for task [$caller_name] with pids [$(joinString , ${pidsArray[@]})]."
soft_alert=true
SendAlert true
fi
if [ $exec_time -gt $hard_max_time ] && [ $hard_max_time -ne 0 ]; then
Logger "Max hard execution time exceeded for task [$caller_name] with pids [$(joinString , ${pidsArray[@]})]. Stopping task execution."
for pid in "${pidsArray[@]}"; do
KillChilds $pid true
if [ $? == 0 ]; then
Logger "Task with pid [$pid] stopped successfully." "NOTICE"
else
Logger "Could not stop task with pid [$pid]." "ERROR"
fi
done
SendAlert true
errrorcount=$((errorcount+1))
fi
fi
for pid in "${pidsArray[@]}"; do
if [ $(IsNumeric $pid) -eq 1 ]; then
if kill -0 $pid > /dev/null 2>&1; then
# Handle uninterruptible sleep state or zombies by ommiting them from running process array (How to kill that is already dead ? :)
#TODO(high): have this tested on *BSD, Mac & Win
pidState=$(ps -p$pid -o state= 2 > /dev/null)
if [ "$pidState" != "D" ] && [ "$pidState" != "Z" ]; then
newPidsArray+=($pid)
fi
else
# pid is dead, get it's exit code from wait command
wait $pid
retval=$?
if [ $retval -ne 0 ]; then
errorcount=$((errorcount+1))
Logger "${FUNCNAME[0]} called by [$caller_name] finished monitoring [$pid] with exitcode [$retval]. "DEBUG"
if [ "$WAIT_FOR_TASK_COMPLETION" == "" ]; then
WAIT_FOR_TASK_COMPLETION="$pid:$retval"
else
WAIT_FOR_TASK_COMPLETION=";$pid:$retval"
fi
fi
fi
fi
done
pidsArray=("${newPidsArray[@]}")
# Trivial wait time for bash to not eat up all CPU
sleep .05
done
# Return exit code if only one process was monitored, else return number of errors
if [ $pidCount -eq 1 ] && [ $errorcount -eq 0 ]; then
return $errorcount
else
return $errorcount
fi
}
Использование:
Давайте возьмем 3 задания сна, получим их pids и отправим в WaitforTaskCompletion:
sleep 10 &
pids="$!"
sleep 15 &
pids="$pids;$!"
sleep 20 &
pids="$pids;$!"
WaitForTaskCompletion $pids 1800 3600 ${FUNCNAME[0]} false 1800
Предыдущий пример предупредит вас, если выполнение займет более 1 часа, остановит выполнение через 2 часа и отправит "живое" сообщение журнала каждые полчаса.
Поскольку на выходе bjobs
это 1 строка (No unfinished job found
), когда нет ожидающих / запущенных заданий, и 2 строки, когда есть хотя бы 1 ожидающее / запущенное задание:
JOBID USER STAT QUEUE FROM_HOST EXEC_HOST JOB_NAME SUBMIT_TIME
25156 awesome RUN best_queue superhost 30*host cool_name Jun 16 05:38
Вы можете зацикливаться bjobs | wc -l
с помощью:
for job in $some_jobs;
bsub < $job
# Waiting for jobs to complete
while [[ `bjobs | wc -l` -ge 2 ]] ; do \
sleep 15
done
done
Одним из преимуществ этого метода является то, что вы можете запускать несколько заданий независимо от того, сколько их нужно запустить. Просто закрепите их, прежде чем ждать. Это явно не самый чистый способ сделать это, но в настоящий момент он работает.
for some_jobs in $job_groups; do \
for job in $some_jobs; do \
bsub < $job
done
# Waiting for jobs to complete
while [[ `bjobs | wc -l` -ge 2 ]] ; do \
sleep 15
done
done