Остановите команду bash, как только (конкретная) ошибка будет записана в stderr [duplicate]

Здесь мой рабочий код:

function IsJsonString(str) {
try {
  var json = JSON.parse(str);
  return (typeof json === 'object');
} catch (e) {
  return false;
}
9
задан Todd A. Jacobs 1 January 2013 в 00:59
поделиться

5 ответов

Ответ Тома Андерсона неплох, но kill $(cat $PIDFILE) произойдет только в моей системе, если foo закончен сам по себе или через Ctrl-C. Для меня работает следующее решение

while read g
do
  if [[ $g =~ bar ]]
  then
    kill $!
  fi
done < <(
  exec foo 2> >(tee /dev/tty)
)
5
ответ дан Community 19 August 2018 в 13:36
поделиться

Мне удалось найти способ сделать это без PID-файлов или со-подпрограмм и таким образом, чтобы они работали во всех POSIX-совместимых оболочках (я пробовал bash и dash). По крайней мере, в системах, которые поддерживают /dev/fd/, но это должно быть почти все из них.

Это немного запутанно, хотя, поэтому я не уверен, что это по вашему вкусу.

(                               # A
    (                           # B
        ( /tmp/foo 2>&1 1>&3 & echo $! >&4 ) |              # C
        ( tee /dev/fd/2 | ( grep -q bar && echo fin >&4 ) ) # D and E
    ) 4>&1 | (                  # F
        read CHILD
        read STATUS
        if [ "$STATUS" = fin ]; then
            kill $CHILD
        fi
    )
) 3>&1

Чтобы объяснить многочисленные подоболочки, используемые здесь:

Тело A работает с нормальным stdout, дублируемым до fd 3. Он запускает подоболочки B и F с выводом B, поданным по каналу на stdin F.

Тело B работает с трубой из A, дублированной на fd 4.

C запускает вашу фактическую команду foo, при этом ее stderr подключается к трубе от C до D и ее stdout, дублируемое из fd 3; то есть восстанавливается на глобальном уровне. Затем он записывает PID foo в fd 4; то есть к трубе, на которой подоболочка F имеет свой stdin.

D запускает команду tee, получая из трубы любое foo печатает на своем stderr. Он копирует этот вывод как на /dev/fd/2 (чтобы он отображался на глобальном stderr), так и на канал, подключенный к подоболочке E.

E greps для бара, а затем, когда найден , пишет fin на fd 4, то есть на канал, который F имеет на своем stdin. Обратите внимание на &&, убедившись, что no fin не записано, если grep встречает EOF, не найдя bar.

F, затем читает PID с C и fin с E. Если финал fin был правильно выведен, он убивает foo.

EDIT: Исправлено отсутствие tee для копирования stderr foo в реальный stderr.

2
ответ дан Dolda2000 19 August 2018 в 13:36
поделиться
  • 1
    Я действительно не думаю, что это должно иметь значение. Это должно работать на любом POSIX-совместимом, если я не ошибаюсь в чем-то. – Dolda2000 31 December 2012 в 00:16
  • 2
    Может быть, возможно, ваша программа foo буферизует свой stderr, когда он подключен к трубе без его промывки? – Dolda2000 31 December 2012 в 00:20
  • 3
    Ну да, этого и следовало ожидать. read STATUS находится там, где F ожидает, что grep найдет bar. Кстати, вам не нужно удалять старые комментарии. :) – Dolda2000 31 December 2012 в 00:22
  • 4
    Если бы я был на вашем месте, я бы отлаживал его, используя различные процессы, но, к сожалению, я не могу воспроизвести его. – Dolda2000 31 December 2012 в 00:48

Использовать Expect для мониторинга стандартной ошибки

Expect предназначен для принятия действий на основе вывода процесса. Самое простое решение - просто позволить Expect запустить процесс, а затем выйти, когда он увидит ожидаемый результат. Например:

expect -c 'set msg {Saw "foo" on stderr. Exiting process.}
           spawn /bin/bash -c "echo foo >&2; sleep 10"
           expect "foo" { puts $msg; exit }'

Если порожденный процесс заканчивается нормально (например, до появления «foo»), то и сценарий Expect также выйдет.

3
ответ дан Todd A. Jacobs 19 August 2018 в 13:36
поделиться

Я изначально написал способ сделать это, что связано с swizzling, но это было не очень хорошо. Некоторые из комментариев относятся к этой версии. Проверьте историю, если вам интересно.

Вот способ сделать это:

(PIDFILE=$(mktemp /tmp/foo.XXXXXX) && trap "rm $PIDFILE" 0 \
   && { foo \
           2> >(tee >(grep -q bar && kill $(cat $PIDFILE)) >&2) \
        & PID=$! && echo $PID >$PIDFILE ; wait $PID || true; })

Хорошее старомодное кошмарное топливо. Что здесь происходит?

  1. В крайних скобках помещено все это в подоболочку; это ограничивает объем переменных для целей hygeine
  2. . Мы создаем временный файл, используя синтаксис, который работает как с GNU, так и с BSD mktemp, и вызывает его PIDFILE
  3. Мы установили ловушку выходного ловушка (которая запускается, когда выходит самая внешняя подоболочка), чтобы удалить файл с именем PIDFILE, снова для hygeine
  4. . Мы запускаем foo; это выполняется в составном заявлении, так что & связывается с foo, а не со всем предыдущим конвейером
  5. . Мы перенаправляем стандартную ошибку foo в замену процесса, которая ждет bar чтобы появиться, а затем убивает foo (из которых более поздней)
  6. Мы фиксируем PID foo в переменной, записываем его в файл с именем PIDFILE, а затем ждите его, поэтому что вся команда ждет foo, чтобы выйти, прежде чем выйти; || true отбрасывает статус выхода ошибки foo, когда это происходит.

Код внутри замещения процесса работает следующим образом:

  1. Во-первых, tee входной сигнал (стандартная ошибка foo), перенаправление стандартного вывода tee на стандартную ошибку, так что стандартная ошибка foo действительно появляется на стандартной ошибке
  2. Отправить копия ввода, идущая к файлу, идущему на другую подстановку процесса ( замещение процесса в подстановке процесса )
  3. В рамках более глубокого замещения процесса сначала выполняйте grep -q на который ищет указанный шаблон и выходит, как только он найдет его (или когда он достигнет конца потока), без печати чего-либо, после чего (если он нашел строку и успешно вышел) оболочка переходит к ...
  4. kill процесс, PID которого зафиксирован в файле с именем PIDFILE, а именно foo
5
ответ дан Tom Anderson 19 August 2018 в 13:36
поделиться
  • 1
    Я думаю, что есть лучший способ сделать это, который помещает grep && kill внутрь подстановки процесса, которая получает 2> из foo; который оставляет поток foo невозмутимым и будет в целом проще. – Tom Anderson 30 December 2012 в 22:35
  • 2
    @ Dolda2000: После выхода -q, grep делает после нахождения первой совпадающей строки. – Tom Anderson 30 December 2012 в 23:03
2
ответ дан Dolda2000 31 October 2018 в 01:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: