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

23

Como posso reescrever esse comando para enviar apenas emails 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?

Consulte Verifique se o canal está vazio e execute um comando nos dados, se não for para um caso mais geral do que o envio de email.

cosmin
fonte
Muito parecido: Faça um pipe condicional no retorno não vazio (no Superusuário )
G-Man diz 'Reinstate Monica'

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 apenas o comando mail se o grep retornou um status de saída que diz ter correspondências como esta:

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, pule as coisas especiais de nomeação e exclusão:

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

Edit: Depois de ver a resposta de Glenn, eu brinquei mais com isso e, aparentemente, atribuir uma variável usando a $()sintaxe retorna o código de saída do comando, para que você possa pular o teste que ele usou para o comprimento da string e usá-lo. Aqui está tudo em um comando:

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

Edit 2: Depois de ver a resposta de Simon, verifiquei meu mailprograma. Ele não se comporta da maneira que ele descreve por padrão, mas tem uma opção para isso. Na página do manual:

-ESe uma mensagem de saída não contiver texto na primeira ou única parte da mensagem, não a envie, mas a descarte silenciosamente, configurando efetivamente a variável skipemptybody na inicialização do programa. Isso é útil para enviar mensagens de scripts iniciados por cron (8).

Tornando isso possível:

mailq | egrep 'rejected|refused' -A 5 -B 5 | mail -E -s 'dd' email@email
Caleb
fonte
1
o poder da colaboração !!
Glenn Jackman
3
Eu sugeriria que essa resposta pudesse ser melhorada movendo a melhor solução para o topo.
Mark Booth
@Wildcard Essa edição foi desnecessária, dado que os parâmetros de entrada mktempnão retornam algo que precisa ser citado.
Caleb
2
Eu sei. Você é um dos poucos que se incomoda em entender que as aspas às vezes são necessárias (e as omite deliberadamente). No entanto, o padrão deve ser citado, a menos que você queira explicitamente solicitar glob(split(var)). Você não precisa dividir palavras ou expandir arquivos aqui, portanto, não as peça. Além disso, precisamos mostrar o caminho certo neste site .
Curinga
@ Faircard Fair o suficiente. Normalmente, corro zshe na verdade não estou obtendo globbing ou divisão de palavras em casos como este, a menos que eu peça por elas, mas por uma questão de respostas aqui e por não conhecer o ambiente de destino citando por princípio, está bem.
Caleb
11

O programa utilitário ifne existe expressamente para esse fim: 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".

Guy Hillyer
fonte
8

Você pode salvar a saída em uma variável e, em seguida, chamar o comando mail se o comprimento da sequência 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.

Glenn Jackman
fonte
1
Adereços para usar uma variável em vez de um arquivo temporário. Isso me fez pensar sobre onde o código de saída vai do comando com o qual você fez a atribuição de variáveis ​​e, aparentemente, é passado adiante! Veja minha resposta atualizada .
Caleb
7

Use mailcom a -Eopção No meu Mac, MAIL (1) diz que o -Esinalizador não enviará email com o corpo vazio.

-E Não envie mensagens com o corpo vazio. Isso é útil para erros de canal dos scripts cron (8).

No meu Mac, executei o seguinte teste. Observe que o arquivo file_does_existexiste, mas file_does__not_existele não existe.

Isso envia um e-mail para mim:

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

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' stefan@example.org
ls: file_does_not_exist: No such file or directory
Stefan Lasiewski
fonte
Caleb venceu você.
Gilles 'SO- stop be evil'
1
Ele só me venceu por 4,75 horas :). Perdi esse detalhe em sua longa resposta.
Stefan Lasiewski
5

Nesse caso em particular, se o seu mailsuportar a -Eopção , use-a. No caso geral, você pode tentar ler um caractere; se houver, inicie o comando de pós-processamento e alimente esse caractere a partir do restante 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 com que essa função funcione mesmo que o primeiro caractere na entrada seja uma nova linha (lembre-se de que a $(…)construção retira as novas linhas do terminal).

Com a maioria dos shells (qualquer coisa que não seja zsh, tanto quanto eu sei), se o arquivo começar com um caractere nulo, esse código acreditará que está vazio. Correção deixada como um exercício para o leitor. (Dica: use odno primeiro subshell e printfimprima o primeiro byte.) (Solução: Como verificar se o pipe está vazio ) Você pode encontrar o mesmo problema se o arquivo começar com um byte que não é um caractere válido no atual localidade; é mais fácil de corrigir executando esse código com LC_ALL=C.

Gilles 'SO- parar de ser mau'
fonte
+1 por ridículo, mas possivelmente útil em outro cenário :) Por curiosidade, por que injetar e testar o ponto em vez de testar uma string vazia? Isso não apresenta um problema se a entrada começar com um ponto? O uso da readajuda poderia simplificar isso?
Caleb
@ Caleb: Sim, eu deveria ter mencionado que estava respondendo à pergunta no título e não à situação específica. Veja minha edição para uma explicação do ponto. Eu não acho que você possa distinguir uma primeira linha vazia de um arquivo vazio readno sh portátil (pode ser possível no bash, ksh ou zsh).
Gilles 'SO- stop be evil'
3

IIRC o mailcomando BSD é interrompido se receber uma mensagem vazia.

Simon Richter
fonte
1
O programa de correio no meu sistema GNU Linux (Heirloom mailx 12.4) não se comporta dessa maneira por padrão, mas tem uma -Eopção para habilitá-lo .
Caleb
1

Recentemente eu aprendi sobre o nome do comando ifneque faz parte do moreutilspacote. Onde você pode usá-lo para resolver esse problema específico.

ifne - executa o comando se a entrada padrão não estiver vazia

exemplo da página de manual,

EXAMPLE
       find . -name core | ifne mail -s "Core files found" root
Rabin
fonte
0

Você pode reescrever seu comando usando test -s /dev/stdinpara verificar se há alguma saída da mailq | greppeça.

- 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
fonte