bash: як поширювати помилки при заміні процесу?


19

Я хочу, щоб мої сценарії оболонки виходили з ладу щоразу, коли команда, виконана з ними, відмовляється.

Як правило, я роблю це з:

set -e
set -o pipefail

(зазвичай я set -uтакож додаю )

Вся справа в тому, що жоден з перерахованих вище не працює з заміщенням процесу. Цей код друкує "нормально" і виходить із кодом повернення = 0, хоча я б хотів, щоб він не зміг:

#!/bin/bash -e
set -o pipefail
cat <(false) <(echo ok)

Чи є щось еквівалентне "pipefail", але для заміни процесу? Будь-який інший спосіб передати команді вихід команд так, як це були файли, але викликати помилку, коли будь-яка з цих програм виходить з ладу?

Рішення бідного чоловіка було б виявити, якщо ці команди пишуть у stderr (але деякі команди записують у stderr в успішних сценаріях).

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


Для видалення названих труб вам не потрібно вловлювати помилки. Насправді це зовсім не хороший спосіб. Розглянемо mkfifo pipe; { rm pipe; cat <file; } >pipe. Ця команда буде висіти , поки читач не відкривається , pipeтому що це оболонка , яка робить open()і тому , як тільки є читач на pipeпФе посилання для pipeİŞ rm«D , а потім catкопіює вхідний_файл до дескриптора оболонки для цієї труби. І все одно, якщо ви хочете поширити помилку з підпроцесу процесу: <( ! : || kill -2 "$$")
mikeserv

Дякуємо за певну видалення названих труб. На жаль, $$підміна для мене не працює, оскільки заміна команди ths не виконується, оскільки команда, яка використовує підстановку процесу, виконується всередині командного конвеєра, який породжується з коду "не оболонки" (python). Ймовірно, я повинен створити підпроцес в python і передавати їх програмно.
juanleon

Тож використовуйте kill -2 0.
mikeserv

На жаль, "убий -2 0" вбив би (сигнал) пітона. Написання обробників сигналів для виконання ділової логіки в багатопотоковому додатку - це не те, чого я з нетерпінням чекаю :-)
juanleon

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

Відповіді:


6

Ви можете вирішити цю проблему лише з цим, наприклад:

cat <(false || kill $$) <(echo ok)
other_command

Низька оболонка сценарію SIGTERMd, перш ніж друга команда може бути виконана ( other_command). echo okВиконується команда «іноді»: Проблема полягає в тому, що процес заміни є асинхронними. Там немає ніякої гарантії , що kill $$команда виконується до того чи після того, як в echo okкоманді. Це питання планування операційних систем.

Розглянемо такий сценарій bash:

#!/bin/bash
set -e
set -o pipefail
cat <(echo pre) <(false || kill $$) <(echo post)
echo "you will never see this"

Вихід цього сценарію може бути:

$ ./script
Terminated
$ echo $?
143           # it's 128 + 15 (signal number of SIGTERM)

Або:

$ ./script
Terminated
$ pre
post

$ echo $?
143

Ви можете спробувати, і через кілька спроб ви побачите два різних замовлення у висновку. У першому сценарій був припинений до того, як дві інші echoкоманди могли записати у дескриптор файлу. У другому команда falseабо killкоманда, ймовірно, були заплановані після echoкоманд.

Або точніше: Системний виклик signal()від Підприємства обслуговування killнаселення , який посилає на SIGTERMсигнал процес раковин був запланований (або був доставлений) пізніше або раніше , ніж відлуння write()системних викликів.

Але скрипт зупиняється, а вихідний код не дорівнює 0. Тому він повинен вирішити вашу проблему.

Ще одне рішення - звичайно, використовувати для цього названі труби. Але, від вашого сценарію, наскільки складно було б реалізувати названі труби або вирішення вище.

Список літератури:


2

Для запису, і навіть якщо відповіді та коментарі були добрими та корисними, я закінчив реалізувати щось трохи інше (у мене були деякі обмеження щодо прийому сигналів у батьківському процесі, про які я не згадував у запитанні)

В основному я закінчив щось подібне:

command <(subcomand 2>error_file && rm error_file) <(....) ...

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


Що б не працювало для вас, як правило, найкращий спосіб. Особливо дякую за повернення та відповідь - селфі - це мої фаворити.
mikeserv

2

Цей приклад показує, як користуватися killразом із trap.

#! /bin/bash
failure ()
{
  echo 'sub process failed' >&2
  exit 1
}
trap failure SIGUSR1
cat < <( false || kill -SIGUSR1 $$ )

Але killне можна передавати код повернення з вашого підпроцеду до батьківського процесу.


Мені подобається ця зміна на загальноприйнятому відповідь
sehe

1

Аналогічним чином, як ви б реалізували $PIPESTATUS/ $pipestatusз оболонкою POSIX, яка не підтримує її , ви можете отримати статус виходу команд, передаючи їх навколо труби:

unset -v false_status echo_status
{ code=$(
    exec 3>&1 >&4 4>&-
    cat 3>&- <(false 3>&-; echo >&3 "false_status=$?") \
             <(echo ok 3>&-; echo >&3 "echo_status=$?")
);} 4>&1
cat_status=$?
eval "$code"
printf '%s_code=%d\n' cat   "$cat_status" \
                      false "$false_status" \
                      echo  "$echo_status"

Що дає:

ok
cat_code=0
false_code=1
echo_code=0

Або ви можете використовувати pipefailта впроваджувати підстановку вручну, як і з оболонками, які не підтримують її:

set -o pipefail
{
  false <&5 5<&- | {
    echo OK <&5 5<&- | {
      cat /dev/fd/3 /dev/fd/4
    } 4<&0 <&5 5<&-
  } 3<&0
} 5<&0
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.