Intereting Posts
Каждое электронное письмо, которое я получаю, отправляется на адрес root@example.com (Postfix on CentOS) Squid: как я могу заблокировать сайт plus.google.com, разрешая доступ к google.com используя переменную «файл», полученную из «for» файла «in», и перейти к другому скрипту не удалось Разница между параметрами командной строки «-» и «-» с префиксом? Как настроить набор компьютеров для netboot? Как я могу использовать подстановочный знак для соответствия только файлам, а не каталогам? Отбросить все ICMP-пакеты? можем ли мы использовать что-то лучше, чем больше для завершения табуляции? Как grep определенную строку _and_ первую строку файла? Установить TZ в соответствии с / etc / localtime Продолжительность / proc / pid / stat Почему regex '. +' Работает так, как ожидалось? Что делает опция ядра CONFIG_NLS_UTF8? UNIX: отправить задание только после завершения предыдущего задания? проверка статуса для команды sleep

Как grep выводить программу, но также нормально выводить вывод?

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

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

Проблема в том, что если я подключаюсь к grep , я больше не могу видеть выход программы, потому что grep потребляет. Я хотел бы как-то отсканировать вывод сообщений об ошибках, но также нормально отображать вывод, так как помимо ошибок есть другие важные сообщения. К сожалению, grep не имеет возможности передавать все свои входные, согласованные и несогласованные строки.

Один из подходов, который я нашел, в основном работает:

 ! my_program | tee /dev/stderr | grep -q "error message" 

Это подает вывод программы в grep но также копирует его на стандартную ошибку, которая не перенаправляется, поэтому я могу ее увидеть. Это нормально, когда стандартный вывод и стандартная ошибка моего сценария идут на терминал (так что неважно, какой из них получает сообщения), но это может вызвать проблемы, если стандартная ошибка и стандартная ошибка перенаправляются в разные места; сообщения будут не в том месте.

Используя bash или POSIX-оболочку и инструменты GNU, есть (сжатый) способ сканирования стандартных и / или стандартных потоков ошибок программы с чем-то вроде grep и соответственно установить статус выхода, но также позволить обеим потокам перейти к своим обычным пунктам?

(Обратите внимание, что my_program записывает свои сообщения об ошибках в стандартную my_program , а не стандартную ошибку, поэтому решение, которое может проверять только стандартный поток вывода, в порядке, хотя и не идеальное. И в случае, если это имеет значение, я делаю это на CentOS 7 .)

… есть (сжатый) способ сканирования стандартных и / или стандартных потоков ошибок программы с чем-то вроде grep и соответственно установить статус выхода, но также позволить обеим потокам перейти к своим обычным пунктам назначения?

… my_program записывает свои сообщения об ошибках в стандартную версию, а не стандартную ошибку, поэтому решение, которое может проверять только стандартный поток вывода, в порядке, хотя и не идеальное.

Мое решение отвечает на выделенную часть выше.

Я думаю, что самый простой способ сделать это через awk :

 myprogram | awk 'BEGIN {status = 0} /error message/ {status = 1} 1; END {exit(status)}' 

Команда awk выводит все, что она вводит точно, как есть, но в конце она выходит со статусом, зависящим от того, является ли сообщение «ошибка» частью входа.

Краткая версия (с более коротким именем переменной):

 myprogram | awk 'BEGIN{s=0} /error message/{s=1} 1; END{exit(s)}' 

Вот несколько лайнеров, которые grep вызывают сообщение об ошибке $MSG на stdout / stderr и делают / не останавливают программу, как только появляется сообщение об ошибке:

 # grep on stdout, do not stop early my_program | awk -vs="$MSG" '$0~s{r=1} 1; END{exit(r)}' # grep on stdout, do stop early my_program | awk -vs="$MSG" '$0~s{exit(1)} 1' # grep on stderr, do not stop early { my_program 2>&1 >&3 | awk -vs="$MSG" '$0~s{r=1} 1; END{exit(r)}' >&2; } 3>&1 # grep on stderr, do stop early { my_program 2>&1 >&3 | awk -vs="$MSG" '$0~s{exit(1)} 1' >&2; } 3>&1 

Заметки:

  • Для всех: поток, на который вы применяете grep , потеряет свой потенциальный статус tty, потому что my_program видит, что он переходит в awk . Это может повлиять на то, как my_program записывает этот поток, например, он может не печатать вращающиеся индикаторы выполнения, поскольку он может предположить, что он не может управлять позицией курсора.

  • Для всех: stdout и stderr не будут объединены и могут быть перенаправлены независимо друг от друга, как обычно.

  • Для всех: исходный код выхода my_program полностью игнорируется. Единственная другая простая альтернатива: выход с ошибкой, если либо my_program выходит с ошибкой, либо появляется сообщение об ошибке . Чтобы получить это поведение, вам нужно включить pipefail в оболочке Bash, которая выполняет конвейер. Например:

     (set -o pipefail; my_program | awk -vs="$MSG" '$0~s{exit(1)} 1') 
  • Для grepping на stderr: простые командные строки выше предполагают, что дескриптор файла 3 не используется. Это обычно принято делать. Чтобы избежать этого предположения, вы можете заставить Bash выделить файловый дескриптор, который, как гарантируется, не будет использоваться:

     bash -c 'exec {fd}>&1; { my_program 2>&1 >&${fd} | awk -vs="$MSG" '\''$0~s{exit(1)} 1'\'' >&2; } ${fd}>&1' 

Попробуйте два терминала рядом друг с другом. В первом окне оболочки:

 tail -F /tmp/xyzzy 

Во втором – только то, что вы делали, только для файла tmp:

 my_program | tee /tmp/xyzzy | grep -q "error message" 

Запустите их в этом порядке.

Это klunky, так как вы хотите очистить временный файл так часто или выбрать новое имя, но оно работает.

Добавлено позже … Попробуйте что-то вроде:

 my_program | tee /tmp/xyzzy ; grep -q "error message" /tmp/xyzzy 

Статус выхода последовательности – это статус выхода grep. Вздохнул. Так что отрицайте это.

 my_program | tee /tmp/xyzzy ; ! grep -q "error message" /tmp/xyzzy 

Создайте демо-функцию baz() которая выводит «foo» на stdout и «bar» на stderr :

 baz() { echo foo ; echo bar >& 2 ; } 

Простой случай, запустите grep foo на stdout и grep bar на stderr :

 { baz 2>&1 1>&3 | grep bar 1>&2 ; } 3>&1 | grep foo 

То же самое, но тихо, используя tee ; вывод выглядит так, как будто никакие grep s не использовались вообще:

 { baz 2>&1 1>&3 | tee /dev/stderr | grep -q bar ; } 3>&1 | \ { tee /dev/stderr | grep -q foo ; } 2>&1 

Теперь добавьте условное grep -q bar после grep -q bar , которая печатает «BEEP!». на stderr, если найден бар :

 { baz 2>&1 1>&3 | tee /dev/stderr | grep -q bar && echo "BEEP!" >&2 ; } 3>&1 | \ { tee /dev/stderr | grep -q foo ; } 2>&1 

Вывод двух последних строк:

 foo bar BEEP!