Как завершить все на определенном уровне подоболочки в Bash, не используя kill 0?

Контекст:

Скажем, у меня есть:

(
#outer subshell
    {
    #inner command group, pipe-connected to ensure simultaneous invocation
        do_first_thing
        #die, somehow
    } | { 
    #inner command group, pipe-connected to ensure simultaneous invocation
        sleep 10
        do_second_thing
    }
)
echo "out"

У меня есть внешняя подоболочка (не обязательно; это может быть группа команд или ее можно полностью удалить). Внутри у меня есть две соединенные конвейером группы команд, которые вызываются одновременно. Я хочу, чтобы обе группы начали выполняться, а затем, когда первая группа завершится (вторая все еще будет sleeping), я хочу, чтобы выполнение внешней подоболочки остановилось, где находится строка #die, somehow.

Вопрос:

Что я должен сделать, чтобы при достижении строки #die, somehow все на уровне #outer subshell завершалось, но выполнение продолжалось после него (т.е. печаталось out)?

Что я пробовал:

Одним из решений является использование kill 0 (убить все в текущей группе процессов), чтобы выйти из обеих внутренних групп команд, но тогда echo "out" никогда не произойдет; вся группа будет уничтожена, а не только внешняя подоболочка.

Я также пытался временно переопределить, а затем unsetобработать функцию exit, но это, похоже, просто переносит проблему в другое место. Возможно, я делаю это неправильно; Я ни в коем случае не гуру Bash.

Почему?

Не вдаваясь в подробности, странные группы команд, связанные с подоболочкой, необходимы, потому что использование & для разветвления процессов не работает детерминировано в моей системе (встроенная * nix). В результате группы, соединенные конвейером, гарантируют, что если do_first_thing будет работать слишком долго (10 секунд в приведенном выше примере), произойдет do_second_thing. Реальный вариант использования значительно сложнее, но именно в этом корень проблемы.

Ваше здоровье!


person Zac B    schedule 13.08.2012    source источник


Ответы (2)


#!/bin/bash                                                                     
(
  #outer subshell                                                                 
  {
    #inner command group, pipe-connected to ensure simultaneous invocation      
    first_step;
    ps -ef | grep './lol.bash' | awk '{print $2}' | xargs kill;
    #die, somehow                                                           
  } | {
    #inner command group, pipe-connected to ensure simultaneous invocation      
    ./lol.bash;
  }
)
echo "out"

и lol.bash содержит ваш сон, а затем так называемый "второй_шаг"... это не очень чисто, но получается.

другой способ - создать цикл во второй части, который проверяет, выполнен ли первый шаг, в течение определенного времени, а затем уходит, если это так, или очищает все на первом этапе и выполняет некоторые действия, если время истекло. .

NB: очевидно, вы можете запускать на каждом этапе отдельный процесс, а затем убивать его, когда захотите... (вам просто нужно знать pid...)

person N4553R    schedule 13.08.2012

Будет ли структура в этом направлении подходящей?

touch /tmp/sentinel-file
(do_first_thing; rm /tmp/sentinel-file) &
pid=$!
sleep 10
if [[ -r /tmp/sentinel-file ]]
then
  kill $pid
  rm /tmp/sentinel-file
  do_second_thing
fi

Там есть некоторые условия гонки, если вы запускаете несколько копий этого, но в некотором смысле это может быть немного чище, чем то, что вы делаете сейчас.

person twalberg    schedule 13.08.2012
comment
Если бы & работало правильно, это сработало бы. Но в моей системе этого нет (см. раздел «Почему»). Если бы я мог правильно предусмотреть вещи, ничего из этого не было бы проблемой. Спасибо! - person Zac B; 14.08.2012
comment
Упс... пропустил это при первом чтении... В таком случае, возможно, изменив do_first_thing для создания файла с его PID (и удалив этот файл перед его выходом), чтобы do_second_thing мог получить PID для kill, это может сработать, если у вас есть исходники программ... - person twalberg; 14.08.2012
comment
Не совсем; идея состоит в том, чтобы группа, содержащая do_first_thing, запускалась в то же время, что и группа, содержащая do_second_thing. Первая группа, которая завершается (время выполнения сильно варьируется, что я представил в примере с оператором sleep), должна закрыть всю внешнюю оболочку. - person Zac B; 14.08.2012