Como canalizar a saída de um processo para outro, mas somente executar se o primeiro tiver saída?

23

Como posso reescrever este comando para enviar um email somente se houver saída do mailq | grep ?

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

Isso é possível em uma linha?

Veja Verifique se o pipe está vazio e execute um comando nos dados, se não for , para um caso mais geral do que enviar email.

    
por cosmin 17.05.2011 / 13:09

8 respostas

19

Eu acho que você terá que usar um arquivo temporário para esta operação, para que você possa usar o operador && para executar somente o comando mail se o grep retornou um status de saída que diz que ele tinha correspondências assim:

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

Se você não se importou com o arquivo temporário em algum lugar e pode usar um nome estático para ele, pode ignorar o material especial de nomeação e exclusão:

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

Editar: depois de ver resposta de glenn Eu joguei com isso um pouco mais e, aparentemente, atribuindo uma variável usando a sintaxe $() retorna o código de saída do comando , então você pode pular o teste que ele usou para o tamanho da string e usá-lo. Aqui está tudo em um só comando:

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

Editar 2: Depois de ver resposta de Simon eu verifiquei o meu programa mail . Ele não se comporta da maneira que ele descreve por padrão, mas tem uma opção para isso. Na página man:

-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).

Tornando isso possível:

mailq | egrep 'rejected|refused' -A 5 -B 5 | mail -E -s 'dd' email@email
    
por 17.05.2011 / 13:32
11

O programa utilitário ifne existe expressamente para este propósito: para executar um comando se a entrada padrão não estiver vazia.

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

O comando ifne faz parte do pacote moreutils , anunciado como "uma coleção crescente de ferramentas Unix que ninguém pensou em escrever há muito tempo, quando o Unix era jovem ".

    
por 11.11.2013 / 06:43
8

Você pode salvar a saída em uma variável e invocar o comando mail se o comprimento da string for diferente de zero.

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

Para uma linha, junte os dois comandos com um ponto e vírgula.

    
por 17.05.2011 / 16:14
7

Use mail com a opção -E . No meu Mac, MAIL (1) diz que o sinalizador -E não enviará e-mails com um corpo vazio.

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

No meu Mac, fiz o seguinte teste. Observe que o arquivo file_does_exist existe, mas o arquivo file_does__not_exist não existe.

Isso envia um email para mim:

$ ls file_does_exist | egrep 'file' -A5 -B5 | mailx -E -s 'test' [email protected]

Isso não acontece. Observe que o comando ls file_does_not_exist | egrep ... não produz nenhuma saída.

$ ls file_does_not_exist | egrep 'file' -A5 -B5 | mailx -E -s 'test' [email protected]
ls: file_does_not_exist: No such file or directory
    
por 17.05.2011 / 22:36
5

Neste caso específico, se o seu mail suportar a opção -E , use-o. No caso geral, você pode tentar ler um caractere; se houver, inicie o comando de pós-processamento e alimente-o com o resto do arquivo.

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

Observe o uso útil de cat . A adição de echo . faz essa função funcionar mesmo se o primeiro caractere na entrada for uma nova linha (lembre-se de que a construção $(…) retira as novas linhas do terminal).

Com a maioria dos shells (qualquer coisa além de zsh, até onde eu sei), se o arquivo começar com um caractere nulo, este código acreditará que está vazio. Corrigir isso é deixado como um exercício para o leitor. (Dica: use od no primeiro subshell e printf para imprimir o primeiro byte.) (Solução: Como verificar se o pipe está vazio ) Você pode ter o mesmo problema se o arquivo começar com um byte que não seja um caractere válido no local atual; isso é mais fácil de corrigir, executando este código com LC_ALL=C .

    
por 17.05.2011 / 22:32
3

IIRC o comando BSD mail é anulado se receber uma mensagem vazia.

    
por 17.05.2011 / 17:03
1

Recentemente, aprendi sobre o nome do comando ifne , que faz parte do pacote moreutils . Onde você pode usá-lo para resolver este problema específico.

ifne - Run command if the standard input is not empty

exemplo da página do manual,

EXAMPLE
       find . -name core | ifne mail -s "Core files found" root
    
por 05.08.2015 / 19:55
0

Você pode reescrever seu comando usando test -s /dev/stdin para verificar se há alguma saída da parte 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
    
por 14.10.2014 / 11:15

Tags