Come convogliare l'output da un processo a un altro ma eseguirlo solo se il primo ha output?


23

Come posso riscrivere questo comando per e-mail solo se c'è output da mailq | grep?

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

È possibile anche su una riga?

Vedi Controlla se pipe è vuota ed esegui un comando sui dati se non è per un caso più generale rispetto all'invio di e-mail.


Risposte:


19

Penso che dovrai usare un file temporaneo per questa operazione in modo da poter usare l' &&operatore per eseguire il comando mail solo se grep ha restituito uno stato di uscita che dice che aveva corrispondenze come questa:

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

Se non ti dispiace che il file temporaneo si trovi da qualche parte e puoi usare un nome statico per esso, potresti saltare le cose speciali di denominazione ed eliminazione:

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

Modifica: Dopo aver visto la risposta di Glenn ho giocato ancora un po 'con questo e apparentemente assegnando una variabile usando la $()sintassi restituisce il codice di uscita del comando, quindi puoi saltare il test che ha usato per la lunghezza della stringa e usarlo invece. Qui è tutto in un solo comando:

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

Modifica 2: Dopo aver visto la risposta di Simon ho controllato il mio mailprogramma. Non si comporta come descritto per impostazione predefinita, ma ha un'opzione per quello. Dalla pagina man:

-ESe un messaggio in uscita non contiene alcun testo nella sua prima o unica parte del messaggio, non inviarlo ma scartalo in silenzio, impostando efficacemente la variabile skipemptybody all'avvio del programma. Ciò è utile per inviare messaggi da script avviati da cron (8).

Rendere possibile questo:

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

1
il potere della collaborazione !!
Glenn Jackman,

3
Vorrei suggerire che questa risposta potrebbe essere migliorata spostando la soluzione migliore verso l'alto.
Mark Booth,

@Wildcard Quella modifica non era necessaria, dato che i parametri di input mktempnon restituiranno qualcosa che deve essere quotato.
Caleb,

2
Lo so. Sei uno dei pochi che si preoccupa di capire che le virgolette sono talvolta necessarie (e le omette deliberatamente). Tuttavia, il valore predefinito dovrebbe essere quotato a meno che non si desideri esplicitamente richiedere glob(split(var)). Non è necessario dividere le parole o espandere il file glob qui, quindi non chiederle. Inoltre, dobbiamo mostrare la strada giusta su questo sito .
Wildcard il

@Wildcard Abbastanza giusto. In genere corro zshe in realtà non ricevo trambusto o frazionamento di parole in casi come questo a meno che non li chieda, ma per amore di risposte qui e non conoscere l'ambiente target che cita per principio va bene.
Caleb,

11

Il programma di utilità ifne esiste espressamente per questo scopo: eseguire un comando se l'input standard non è vuoto.

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

Il comando ifne fa parte del pacchetto moreutils , definito come "una raccolta crescente di strumenti Unix che nessuno pensava di scrivere molto tempo fa, quando Unix era giovane".


8

È possibile salvare l'output in una variabile e quindi invocare il comando mail se la lunghezza della stringa è diversa da zero.

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

Per una riga, unisci i due comandi con un punto e virgola.


1
Puntelli per l'utilizzo di una variabile anziché di un file temporaneo. Questo mi ha fatto pensare a dove va il codice di uscita dal comando con cui hai fatto l'assegnazione della variabile, e apparentemente viene passato! Vedi la mia risposta aggiornata .
Caleb,

7

Utilizzare mailcon l' -Eopzione. Sul mio Mac, MAIL (1) dice che il -Eflag non invierà e-mail con un corpo vuoto.

-E Non inviare messaggi con un corpo vuoto. Ciò è utile per errori di piping dagli script cron (8).

Sul mio Mac, ho eseguito il seguente test. Si noti che il file file_does_existesiste, ma il file file_does__not_existnon esiste.

Questo mi invia una email:

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

Questo no. Si noti che il comando ls file_does_not_exist | egrep ...non produce alcun output.

$ 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
Mi ha battuto solo per 4,75 ore :). Ho perso questo dettaglio nella sua lunga risposta.
Stefan Lasiewski,

5

In questo caso particolare, se mailsupporta l' -Eopzione , basta usarla. Nel caso generale, puoi provare a leggere un carattere; se ce n'è uno, avvia il comando postprocessing e inseriscilo nel resto del 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
  fi
}

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

Nota l'uso utile di cat. L'aggiunta di echo .questa funzione funziona anche se il primo carattere nell'input è una nuova riga (ricordate che il $(…)costrutto elimina le nuove linee terminali).

Con la maggior parte delle shell (qualsiasi cosa diversa da zsh, per quanto ne so), se il file inizia con un carattere null, questo codice crederà che sia vuoto. Correzione che viene lasciata come esercizio per il lettore. (Suggerimento: utilizzare odnella prima subshell e printfstampare il primo byte.) (Soluzione: come verificare se la pipe è vuota ) È possibile che si verifichi lo stesso problema se il file inizia con un byte che non è un carattere valido nell'attuale impostazioni locali; è più facile da risolvere eseguendo questo codice con LC_ALL=C.


+1 per ridicolo ma forse utile in un altro scenario :) Per curiosità, perché iniettare e testare il punto invece di testare una stringa vuota? Questo non introduce un problema se l'input inizia con un punto? L'utilizzo readdell'aiuto potrebbe semplificarlo?
Caleb,

@Caleb: Sì, avrei dovuto menzionare che stavo rispondendo alla domanda nel titolo e non alla situazione particolare. Vedi la mia modifica per una spiegazione del punto. Non penso che tu possa distinguere una prima riga vuota da un file vuoto con readin sh portatile (potrebbe essere possibile in bash, ksh o zsh).
Gilles 'SO- smetti di essere malvagio'

3

IIRC il mailcomando BSD si interrompe se viene visualizzato un messaggio vuoto.


1
Il programma di posta sul mio sistema GNU Linux (Heirloom mailx 12.4) non si comporta in questo modo per impostazione predefinita, ma ha un -Eopzione per attivarlo .
Caleb,

1

Recentemente ho imparato a conoscere il nome del comando ifneche fa parte del moreutilspacchetto. Dove puoi usarlo per risolvere questo problema specifico.

ifne : esegue il comando se l'input standard non è vuoto

esempio dalla pagina man,

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

0

È possibile riscrivere il comando usando test -s /dev/stdinper verificare se c'è qualche output dalla mailq | grepparte.

- 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.