Как передать вывод из одного процесса в другой, но выполнить только в том случае, если первый имеет выход?

23

Как я могу переписать эту команду только для электронной почты, если есть выход из mailq | grep?

mailq | egrep 'rejected|refused' -A 5 -B 5 | mail -s 'dd' email@email

Это возможно даже на одной линии?

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

Космин
источник
Очень похоже: Сделать условную трубу на непустом возвращении (на Super User )
G-Man говорит «восстановила Монику»

Ответы:

19

Я думаю, что вам нужно будет использовать временный файл для этой операции, чтобы вы могли использовать &&оператор для запуска почтовой команды только в том случае, если grep вернул состояние выхода, которое говорит, что совпадения были такими:

TMPFILE=`mktemp /tmp/mailqgrep.XXXXXX`; mailq | egrep 'rejected|refused' -A5 -B5 > "$TMPFILE" && mail -s 'dd' email@email < "$TMPFILE"; rm "$TMPFILE"

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

 mailq | egrep 'rejected|refused' -A5 -B5 > /tmp/mailqgrep && mail -s 'dd' email@email < /tmp/mailqgrep

Изменить: После просмотра ответа Гленна я поиграл с этим еще немного и, видимо, присвоение переменной с использованием $()синтаксиса возвращает код завершения команды, так что вы можете пропустить тест, который он использовал для длины строки, и использовать его вместо этого. Вот это все в одной команде:

data=$(mailq | egrep 'rejected|refused' -A 5 -B 5) && mail -s 'dd' email@email <<< "$data"

Изменить 2: увидев ответ Саймона, я проверил свою mailпрограмму. Он не ведет себя так, как он описывает по умолчанию, но имеет возможность для этого. Со страницы руководства:

-EЕсли исходящее сообщение не содержит никакого текста в своей первой или единственной части сообщения, не отправляйте его, а отбрасывайте его молча, эффективно устанавливая переменную skipemptybody при запуске программы. Это полезно для отправки сообщений из скриптов, запускаемых cron (8).

Делая это возможным:

mailq | egrep 'rejected|refused' -A 5 -B 5 | mail -E -s 'dd' email@email
Калеб
источник
1
сила сотрудничества !!
Гленн Джекман
3
Я бы предположил, что этот ответ можно улучшить, перенеся лучшее решение на вершину.
Марк Бут
@Wildcard Это редактирование не требовалось, поскольку входные параметры mktempне возвращают то, что нужно в кавычках.
Калеб
2
Я знаю. Вы один из немногих , кто потрудился понять , что котировки являются иногда необходимо (и опускает них преднамеренно). Однако по умолчанию следует заключать в кавычки, если вы явно не хотите вызывать glob(split(var)). Вам не нужно разделять слова или расширять глобусы файлов, поэтому не спрашивайте их. Также нам нужно показать правильный путь на этом сайте .
Подстановочный
@ Wildcard Достаточно справедливо. Я обычно бегаю zshи на самом деле не получаю ссор или расщепления слов в подобных случаях, если только я не прошу их, но ради ответов здесь и незнания целевой среды цитирование из принципа - это нормально.
Калеб
11

Утилита ifne существует специально для этой цели: запустить команду, если стандартный ввод не пустой.

mailq | egrep 'rejected|refused' -A 5 -B 5 | ifne mail -s 'dd' email@email

Команда ifne является частью пакета moreutils , объявленного «растущей коллекцией инструментов Unix, о которой никто не думал писать давно, когда Unix был молодым».

Гай Хиллиер
источник
8

Вы можете сохранить выходные данные в переменной, а затем вызвать команду mail, если длина строки не равна нулю.

data=$(mailq | egrep 'rejected|refused' -A 5 -B 5)
[[ -n "$data" ]] && mail -s 'dd' email@email <<< "$data"

В одну строку соедините две команды точкой с запятой.

Гленн Джекман
источник
1
Реквизит для использования переменной вместо временного файла. Это заставило меня задуматься о том, куда идет код выхода из команды, с которой вы сделали присвоение переменной, и, очевидно, он передается! Смотрите мой обновленный ответ .
Калеб
7

Используйте mailс -Eопцией. На моем Mac MAIL (1) говорит, что -Eфлаг не будет отправлять электронную почту с пустым телом.

-E Не отправлять сообщения с пустым телом. Это полезно для передачи ошибок из скриптов cron (8).

На моем Mac я провел следующий тест. Обратите внимание, что файл file_does_existсуществует, но файл file_does__not_existне существует.

Это отправляет мне электронное письмо:

$ ls file_does_exist | egrep 'file' -A5 -B5 | mailx -E -s 'test' stefan@example.org

Это не так. Обратите внимание, что команда ls file_does_not_exist | egrep ...не производит никаких выходных данных.

$ ls file_does_not_exist | egrep 'file' -A5 -B5 | mailx -E -s 'test' stefan@example.org
ls: file_does_not_exist: No such file or directory
Стефан Ласевский
источник
Калеб побил тебя этим.
Жиль "ТАК - перестань быть злым"
1
Он побил меня только на 4,75 часа :). Я упустил эту деталь в его длинном ответе.
Стефан Ласевский
5

В этом конкретном случае, если вы mailподдерживаете -Eопцию , просто используйте ее. В общем случае вы можете попытаться прочитать один символ; если он есть, запустите команду постобработки и передайте ему этот символ из остальной части файла.

pipe_if_not_empty () {
  a=$(dd bs=1 count=1 2>/dev/null; echo .)  # read at most one character
  if [ "$a" != "." ]; then                  # if there were two characters,
    { printf %s "${a%.}";                   # then output the first character
      cat; } |                              # and the rest of the input   
    "$@"                                    # into the specified program
  fi
}

mailq | egrep 'rejected|refused' -A 5 -B 5 |
pipe_if_not_empty mail -s 'dd' email@email

Обратите внимание на полезное использование cat. Добавление echo .заставляет эту функцию работать, даже если первый символ во вводе является новой строкой (помните, что $(…)конструкция удаляет новые строки терминала).

В большинстве оболочек (насколько мне известно, кроме zsh), если файл начинается с нулевого символа, этот код будет считать его пустым. Исправление оставлено в качестве упражнения для читателя. (Подсказка: используйте odв первой подоболочке и printfнапечатайте первый байт.) (Решение: Как проверить, пуст ли канал ). Вы можете столкнуться с той же проблемой, если файл начинается с байта, который не является допустимым символом в текущем локали; это легче исправить, запустив этот код с LC_ALL=C.

Жиль "ТАК - перестань быть злым"
источник
+1 для смешного, но, возможно, полезного в другом сценарии :) Из любопытства, зачем вводить и проверять точку, а не проверять пустую строку? Разве это не создает проблемы, если ввод начинается с точки? Может ли readпомощь помочь упростить это?
Калеб
@Caleb: Да, я должен был упомянуть, что отвечаю на вопрос в названии, а не на конкретную ситуацию. Смотрите мое редактирование для объяснения точки. Я не думаю, что вы можете отличить пустую первую строку от пустого файла с помощью readпереносимого sh (это может быть возможно в bash, ksh или zsh).
Жиль "ТАК - перестань быть злым"
3

IIRC команда BSD mailпрерывается, если дано пустое сообщение.

Саймон Рихтер
источник
1
Почтовая программа на моей системе GNU Linux (Фамильные mailx 12.4) не ведет себя таким образом , по умолчанию, но есть в -Eпараметр , чтобы включить его .
Калеб
1

Недавно я узнал об имени команды, ifneкоторая является частью moreutilsпакета. Где вы можете использовать его для решения этой конкретной проблемы.

ifne - запустить команду, если стандартный ввод не пуст

пример из справочной страницы,

EXAMPLE
       find . -name core | ifne mail -s "Core files found" root
Рабин
источник
0

Вы можете переписать свою команду, используя, test -s /dev/stdinчтобы проверить, есть ли какие-либо выходные данные из mailq | grepдетали.

- mailq | egrep 'rejected|refused' -A 5 -B 5 | mail -s 'dd' email@email
+ mailq | egrep 'rejected|refused' -A 5 -B 5 | (test -s /dev/stdin && cat) | mail -s 'dd' email@email
trent55
источник