Выход из конвейера, если предыдущая команда не выполнена

Выход из конвейера, если предыдущая команда не выполнена

Я пытаюсь проверить количество запущенных и поставленных в очередь заданий PBS, анализируя вывод qstat -tn1из bashскрипта. Пока это сработало:

count ()
{
    qstat -tn1 | awk '
        BEGIN { R = 0; Q = 0; }
        $10 == "R" { R++ }
        $10 == "Q" { Q++ }
        END { print R, Q }'
}

if read -r R Q < <(count)
    ...

Однако я вижу, что qstatиногда это не удается по неизвестным причинам. В этом случае он ничего не печатает в stdoutи какое-то сообщение об ошибке в stderrи завершается с ненулевым статусом (довольно стандартно). Однако awkне знает, что qstatпроизошел сбой, и с радостью печатает 0 0для пустого ввода, который он получил. Затем readприсваивает 0 обоим Rи Qне зная, что qstatна самом деле произошел сбой.

  1. Мне нужно инициализировать Rи Qзначением 0 в BEGINблоке скрипта awk, поскольку может не быть запущенных процессов или процессов в очереди, и мне нужно вывести 0, а не просто пустую строку, количество таких процессов.
  2. Я мог бы сделать это set -o pipefail, что позволило бы countвыйти с ненулевым статусом, но readне смог бы увидеть статус выхода и в любом случае awkбыл бы выполнен и выведен на экран 0 0для пустого ввода.
  3. Я мог бы попробовать именованный канал и подпроцессы, но необходимость управлять ими кажется мне излишеством и слишком сложным.

Есть ли хороший способ позволить вызывающему объекту countобнаружить его сбой?

решение1

Я думаю, что чтение countв подстановке процесса не позволяет вам получить его статус возврата. Так что не делайте этого. Вместо этого сохраните результат в переменной или используйте канал.

count=$(count)
if [ $? -eq 0 ]; then
  read -r R Q <<<"$count"

или

set -o pipefail
if count | { read -r R Q; … }

Другая возможность — использовать PIPESTATUSпеременную для проверки статуса возврата первой команды.

count=$(qstat -tn1 | awk …)
if ((${PIPESTATUS[0]} == 0)); then
  read P Q

В качестве альтернативы можно настроить awk так, чтобы он выводил что-то отличительное (например, ничего), когда его входные данные пусты.

awk '
    BEGIN { R = 0; Q = 0; }
    $10 == "R" { R++ }
    $10 == "Q" { Q++ }
    END { if (NR) print R, Q }'

Вы можете проверить, является ли ввод команды пустым, с помощью ifnefrommoreutilsилидругие методы. Но поскольку вы подключаетесь к awk, вы могли бы сделать это прямо внутри скрипта awk, который у вас уже есть.

Если вам нужно получить статус возврата от qstatкоманды, вы можете передать его awk как дополнительную строку ввода. Для облегчения разбора сделайте так, чтобы последняя строка имела уникальный формат.

{
  qstat -tn1
  echo exit_code = $?
} | awk '
    /^exit_code = / { status = $3 }
    END { if (status == 0) print Q, R }
'

решение2

Этот:

count ()
{
    { qstat -tn1 || echo "EPIC FAIL" } | awk '
        BEGIN { R = 0; Q = 0; }
        $10 == "R" { R++ }
        $10 == "Q" { Q++ }
        END { print R, Q }'
}

if read -r R Q < <(count)
    ...

В случае qstatуспеха его вывод отправляется в awk. Если qstatвозвращается ненулевой статус, текст «EPIC FAIL» отправляется в awk.

Это всего лишь пример. Замените эхо чем-то подходящим, что вы можете обработать внутри awkили с помощью if read.

Связанный контент