¿Cómo canalizar la salida de un proceso a otro pero solo ejecutar si el primero tiene salida?

23

¿Cómo puedo reescribir este comando para enviar solo por correo electrónico si hay salida del mailq | grep?

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

¿Es esto posible en una línea?

Consulte Verificar si la tubería está vacía y ejecute un comando en los datos si no es para un caso más general que el envío de correo electrónico.

cosmin
fuente
Muy similar: haga que una tubería sea condicional en el retorno no vacío (en Super Usuario )
G-Man dice 'Reinstate Monica'

Respuestas:

19

Creo que tendrá que usar un archivo temporal para esta operación para que pueda usar el &&operador para ejecutar solo el comando de correo si el grep devolvió un estado de salida que dice que tenía coincidencias como esta:

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

Si no le importaba que el archivo temporal se quedara en algún lugar y puede usar un nombre estático para él, puede omitir las cosas especiales de nomenclatura y eliminación:

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

Editar: después de ver la respuesta de Glenn, jugué con esto un poco más y, aparentemente, asignar una variable usando la $()sintaxis devuelve el código de salida del comando, por lo que puede omitir la prueba que utilizó para la longitud de la cadena y usarla en su lugar. Aquí está todo en un comando:

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

Edición 2: después de ver la respuesta de Simon, revisé mi mailprograma. No se comporta de la manera que él describe de manera predeterminada, pero tiene una opción para eso. Desde la página del manual:

-ESi un mensaje saliente no contiene ningún texto en su primera o única parte del mensaje, no lo envíe, deséchelo en silencio, configurando efectivamente la variable skipemptybody al inicio del programa. Esto es útil para enviar mensajes desde scripts iniciados por cron (8).

Haciendo esto posible:

mailq | egrep 'rejected|refused' -A 5 -B 5 | mail -E -s 'dd' email@email
Caleb
fuente
1
¡El poder de la colaboración!
Glenn Jackman
3
Sugeriría que esta respuesta podría mejorarse moviendo la mejor solución al principio.
Mark Booth
@Wildcard Esa edición no fue necesaria, dado que los parámetros de entrada mktempno devolverán algo que necesita ser citado.
Caleb
2
Lo sé. Usted es uno de los pocos que se molesta en comprender que las citas a veces son necesarias (y las omite deliberadamente). Sin embargo, el valor predeterminado debe estar entre comillas a menos que desee llamarlo explícitamente glob(split(var)). No necesita división de palabras o expansión global de archivos aquí, así que no las solicite. Además, debemos mostrar la forma correcta en este sitio .
Comodín el
@Wildcard Bastante justo. Por lo general, corro zshy en realidad no obtengo problemas o división de palabras en casos como este, a menos que las pida, pero por el bien de las respuestas aquí y no saber el entorno objetivo citado por principio está bien.
Caleb
11

El programa de utilidad ifne existe expresamente para este propósito: ejecutar un comando si la entrada estándar no está vacía.

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

El comando ifne es parte del paquete moreutils , considerado como "una colección creciente de herramientas de Unix que nadie pensó escribir hace mucho tiempo, cuando Unix era joven".

Guy Hillyer
fuente
8

Podría guardar el resultado en una variable y luego invocar el comando de correo si la longitud de la cadena no es cero.

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

Para una línea, une los dos comandos con un punto y coma.

Glenn Jackman
fuente
1
Accesorios para usar una variable en lugar de un archivo temporal. Esto me hizo pensar en dónde va el código de salida del comando con el que hiciste la asignación de variables, ¡y aparentemente se pasa! Ver mi respuesta actualizada .
Caleb
7

Usar mailcon la -Eopción. En mi Mac, MAIL (1) dice que la -Ebandera no enviará correos electrónicos con un cuerpo vacío.

-E No envíe mensajes con un cuerpo vacío. Esto es útil para canalizar errores de scripts cron (8).

En mi Mac, ejecuté la siguiente prueba. Tenga en cuenta que el archivo file_does_existexiste, pero el archivo file_does__not_existno existe.

Esto me envía un correo electrónico:

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

Esto no lo hace. Tenga en cuenta que el comando ls file_does_not_exist | egrep ...no produce ningún resultado.

$ 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
fuente
Caleb te ganó.
Gilles 'SO- deja de ser malvado'
1
Él solo me ganó por 4.75 horas :). Extrañé este detalle en su larga respuesta.
Stefan Lasiewski
5

En este caso particular, si mailadmite la -Eopción , simplemente úsela. En el caso general, puede intentar leer un carácter; si hay uno, inicie el comando de postprocesamiento y alimente ese carácter del resto del archivo.

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

Tenga en cuenta el uso útil de cat. La adición de echo .hace que esta función funcione incluso si el primer carácter en la entrada es una nueva línea (recuerde que la $(…)construcción elimina las nuevas líneas terminales).

Con la mayoría de los shells (que no sea zsh, que yo sepa), si el archivo comienza con un carácter nulo, este código creerá que está vacío. Arreglo que queda como ejercicio para el lector. (Sugerencia: use oden la primera subshell e printfimprima el primer byte.) (Solución: Cómo verificar si la tubería está vacía ) Puede encontrar el mismo problema si el archivo comienza con un byte que no es un carácter válido en el actual lugar; eso es más fácil de solucionar ejecutando este código con LC_ALL=C.

Gilles 'SO- deja de ser malvado'
fuente
+1 para ridículo pero posiblemente útil en otro escenario :) Por curiosidad, ¿por qué inyectar y probar el punto en lugar de probar una cadena vacía? ¿No introduce eso un problema si la entrada comienza con un punto? ¿Podría usar la readayuda simplificar esto?
Caleb
@Caleb: Sí, debería haber mencionado que estaba respondiendo a la pregunta en el título y no a la situación particular. Vea mi edición para una explicación del punto. No creo que pueda distinguir una primera línea vacía de un archivo vacío con readsh portátil (puede ser posible en bash, ksh o zsh).
Gilles 'SO- deja de ser malvado'
3

IIRC el mailcomando BSD aborta si se le da un mensaje vacío.

Simon Richter
fuente
1
El programa de correo en mi sistema GNU Linux (Heirloom mailx 12.4) no se comporta de esta manera por defecto, pero tiene una -Eopción para habilitarlo .
Caleb
1

Recientemente he aprendido sobre el nombre del comando ifneque forma parte del moreutilspaquete. Donde puede usarlo para resolver este problema específico.

ifne : ejecuta el comando si la entrada estándar no está vacía

ejemplo de la página del manual,

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

Puede reescribir su comando usando test -s /dev/stdinpara verificar si hay algún resultado de la 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
trent55
fuente