Програма вбивства після виведення заданого рядка із сценарію оболонки


16

Фон:

Я пишу тестовий сценарій для програмного забезпечення для обчислювальної біології. Програмне забезпечення, яке я тестую, може зайняти кілька днів або навіть тижнів, тому воно має вбудований функціонал відновлення у разі збоїв системи або збоїв живлення.

Я намагаюся розібратися, як протестувати систему відновлення. Зокрема, я не можу визначити спосіб контрольованим чином "збоїти" програму. Я думав якось приурочити інструкцію SIGKILL до запуску через деякий час. Це, мабуть, не ідеально, оскільки тестовий випадок не гарантовано працювати з однаковою швидкістю кожен раз (він працює в спільному середовищі), тому порівняти журнали з потрібним результатом буде складно.

Це програмне забезпечення НЕ друкує рядок для кожного розділу, який він завершує.

Питання:

Мені було цікаво, чи існує хороший / елегантний спосіб (у сценарії оболонки), щоб захопити вихід з програми, а потім вбити програму, коли заданий рядок / # рядків виводиться програмою?


1
Чому потрібно зупинити програму в певний час? Через буферизацію вихідних даних ви можете закінчити програму програми через довгий час після виведення тексту, який ви перевіряєте.
stardt

@stardt Я хотів зупинити програму на певний час, щоб результати тестів були більш контрольованими та відтворюваними. Зараз я знайшов шлях. Я вбиваю програму лише один раз вручну, потім тестую лише код відновлення. Усі спроби відновлення розпочнуться з тієї ж точки. Я тестую, як воно відновлюється, а не як він руйнується зрештою :)
Павло

Відповіді:


7

Такий сценарій обгортки, як цей, є стандартним підходом. Сценарій запускає програму у фоновому режимі, а потім циклічно, щохвилини перевіряючи файл журналу на наявність якогось рядка. Якщо рядок знайдено, фонова програма знищується, а сценарій закривається.

command="prog -foo -whatever"
log="prog.log"
match="this is the what i want to match"

$command > "$log" 2>&1 &
pid=$!

while sleep 60
do
    if fgrep --quiet "$match" "$log"
    then
        kill $pid
        exit 0
    fi
done

15

Якщо ваша програма закінчиться на SIGPIPE (що є дією за замовчуванням), її повинно вистачити для передачі виводу в зчитувач, який вийде при читанні цього рядка.

Тож це може бути так само просто

$ program | sed -e '/Suitable text from the line/q'

Якщо ви хочете придушити використання вихідних даних за замовчуванням

$ program | sed -n -e '/Suitable text from the line/q'

Так само, якщо хтось хоче зупинитися після певної кількості ліній, можна використовувати голову замість sed, наприклад

$ program | head -n$NUMBER_OF_LINES_TO_STOP_AFTER

Точний час, в який відбувається вбивство , залежить від буферної поведінки терміналу, як стверджує Stardt у коментарях.


-1. Тут є серйозна вада. Програма припиняється лише тоді, коли (якщо) вона намагається записати в (зламану) трубу після sedприпинення. ОП каже: "Це програмне забезпечення друкує рядок для кожного розділу аналізу, який він завершує". Це може означати, що програма заповнить один додатковий розділ! Доказ концепції: { echo 1; sleep 3; >&2 echo peekaboo; sleep 3; echo 2; } | sed -e '/1/q'. Дивіться цю відповідь . Можливий обхідний шлях: ( program & ) | sed -e '/Suitable text from the line/q'; killall program. Повідомте свою відповідь про недолік, і я скасую свою прихильність.
Каміль Маціоровський

Хммм ... справді. І питання буферизації робить це ненадійним навіть при вашому поліпшенні (хоча, звичайно, це впливає на кожне рішення, яке я бачу тут). Мені подобається використовувати сигнальну інфраструктуру, коли це можливо, але в якийсь момент вона починає втрачати свою елегантність. Можливо, краще просто зірвати всю справу.
dmckee --- кошеня колишнього модератора

7

В якості альтернативи відповіді dmckee також може бути використана grepкоманда з -mопцією (див., Наприклад, ця командна сторінка ):

compbio | grep -m 1 "Text to match"

зупиняється, коли знайдено 1 рядок, що відповідає тексту, або

compbio | grep -v -m 10 "Text to match"

чекати 10 рядків, які не відповідають заданому тексту.


3

Ви можете використовувати expect(1)для перегляду строку програми, а потім виконувати заздалегідь задані дії на деяких шаблонах.

#!/usr/bin/env expect

spawn your-program -foo -bar 
expect "I want this text" { close }

Або однорозмір для вбудовування в інший сценарій:

expect -c "spawn your-program -foo -bar; expect \"I want this text\" { close }"

Зауважте, що expectза замовчуванням команда не постачається з кожною ОС. Можливо, вам доведеться встановити його.


Це приємне рішення. Рекомендую згадати set timeout -1про встановлення невизначеного тайм-ауту, інакше все закриється за expectзамовчуванням 10-ти тайм-аут.
pepper_chico

1

Ви можете використовувати оператор "while":

Вам потрібно передати висновок свого програмного забезпечення (або його журналів), а потім, для кожного нового рядка, зробити тест умовного стану, а потім виконати kill -KILL. Наприклад (я протестував / var / log / messages як файл реєстрації):

tail -f /var/log/messages | while read line; do test "$line" = "THE LINE YOU WANT TO MATCH" && kill PIDTOKILL; done

Або безпосередньо з програмним забезпеченням:

./biosoftware | while read line; do test "$line" = "THE LINE YOU WANT TO MATCH" && kill PIDTOKILL; done

Вам потрібно замінити шлях журналу / ім'я програми, рядок, який відповідає, та PID для вбивства (ви також можете використовувати killall).

Удачі з вашим програмним забезпеченням,

Гюго,

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.