Sign up ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

How can I rewrite this command to only email if there is output from the mailq | grep?

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

Is this even possible on one line?

share|improve this question

8 Answers 8

I think you will have to use a temp file for this operation so that you can use the && operator to only run the mail command if the grep returned an exit status that says it had matches like this:

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

If you didn't mind the temp file sticking around somewhere and can use a static name for it, you could skip the special naming and deletion stuff:

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

Edit: After seeing glenn's answer I played with this some more and apparently assigning a variable using the $() syntax returns the exit code of the command, so you can skip the test he used for the string length and use that instead. Here it is all in one command:

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

Edit 2: After seeing Simon's answer I checked out my mail program. It does not behave in the way he describes by default, but does have an option for that. From the man page:

-E If an outgoing message does not contain any text in its first or only message part, do not send it but discard it silently, effectively setting skipemptybody variable at program startup. This is useful for sending messages from scripts started by cron(8).

Making this possible:

mailq | egrep 'rejected|refused' -A 5 -B 5 | mail -E -s 'dd' email@email
share|improve this answer
thanks it worked – cosmin May 17 '11 at 11:41
the power of collaboration!! – glenn jackman May 17 '11 at 15:10
I would suggest this answer could be made better by moving the best solution up to the top. – Mark Booth May 18 '11 at 13:16

You could save the output in a variable, and then invoke the mail command if the length of the string is non-zero.

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

For one line, join the two commands with a semi-colon.

share|improve this answer
Props for using a variable instead of a temp file. This got me thinking about where the exit code goes from the command that you did the variable assignment with, and apparently it gets passed on! See my updated answer. – Caleb May 17 '11 at 14:54

The utility program ifne exists expressly for this purpose: to run a command if the standard input is not empty.

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

The ifne command is part of the moreutils package, billed as "a growing collection of Unix tools that nobody thought to write long ago, when Unix was young."

share|improve this answer

Use mail with the -E option. On my Mac, MAIL(1) says the -E flag will not send email with an empty body.

-E Do not send messages with an empty body. This is useful for piping errors from cron(8) scripts.

On my Mac, I ran the following test. Note that the file file_does_exist does exist, but the file file_does__not_exist does not exist.

This sends an email to me:

$ ls file_does_exist | egrep 'file' -A5 -B5 | mailx -E -s 'test'

This does not. Note that the command ls file_does_not_exist | egrep ... does not produce any output.

$ ls file_does_not_exist | egrep 'file' -A5 -B5 | mailx -E -s 'test'
ls: file_does_not_exist: No such file or directory
share|improve this answer
Caleb beat you to it. – Gilles May 17 '11 at 20:48
He only beat me by 4.75 hours :). I missed this detail in his lengthy answer. – Stefan Lasiewski May 17 '11 at 20:53

IIRC the BSD mail command aborts if given an empty message.

share|improve this answer
The mail program on my GNU Linux system (Heirloom mailx 12.4) does not behave this way by default, but does have a -E option to enable it. – Caleb May 17 '11 at 15:19

In this particular case, if your mail supports the -E option, just use it. In the general case, you can try to read one character; if there is one, start the postprocessing command and feed it that character from the rest of the file.

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

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

Note the useful use of cat. The addition of echo . makes this function work even if the first character in the input is a newline (remember that the $(…) construct strips terminal newlines).

With most shells (anything other than zsh, as far as I know), if the file begins with a null character, this code will believe it's empty. Fixing that is left as an exercise for the reader. (Hint: use od in the first subshell and printf to print the first byte.) (Solution: How to check if pipe is empty) You might run into the same issue if the file begins with a byte that is not a valid character in the current locale; that's easier to fix by running this code with LC_ALL=C.

share|improve this answer
+1 for ridiculous but possibly useful in another scenario :) Out of curiosity, why inject and test for the dot instead of testing for an empty string? Doesn't that introduce a problem if the input starts with a dot? Could using read help simplify this? – Caleb May 18 '11 at 13:20
@Caleb: Yes, I should have mentioned I was replying to the question in the title and not to the particular situation. See my edit for an explanation of the dot. I don't think you can distinguish an empty first line from an empty file with read in portable sh (it may be possible in bash, ksh or zsh). – Gilles May 18 '11 at 15:00

You could rewrite your command using test -s /dev/stdin to check if there is any output from the mailq | grep part.

- 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
share|improve this answer

Recently I have learned about the command name ifne which is part of the moreutils package. Where you can use it to solve this specific problem.

ifne - Run command if the standard input is not empty

example from the man page,

       find . -name core | ifne mail -s "Core files found" root
share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.