Hacer una tubería condicional en el retorno no vacío

Respuestas:

9

La siguiente ifnotemptyfunción canaliza su entrada al comando pasado como argumento, excepto que no hace nada si la entrada está vacía. Utilizarlo para tubería source --fooen sink --barpor escrito source --foo | pipe_if_not_empty sink --bar.

pipe_if_not_empty () {
  head=$(dd bs=1 count=1 2>/dev/null; echo a)
  head=${head%a}
  if [ "x$head" != x"" ]; then
    { printf %s "$head"; cat; } | "$@"
  fi
}

Notas de Diseño:

  • Esperaría que esta implementación funcione en todas las plataformas POSIX / Unix, aunque estrictamente hablando no cumple con los estándares: se basa en ddno leer más del byte que se le dice que lea en su entrada estándar.
  • Creo que head -c 1sería un reemplazo adecuado para dd bs=1 count=1 2>/dev/nullLinux.
  • Por otro lado, head -n 1no sería adecuado porque headnormalmente almacena su entrada y puede leer más de la línea que emite, y dado que está leyendo desde una tubería, los bytes adicionales simplemente se pierden.
  • read -r heade incluso read -r -n 1 headno son adecuados aquí porque si el primer carácter es una nueva línea, headse establecería en la cadena vacía, lo que hace imposible distinguir entre la entrada vacía y la entrada que comienza con una línea en blanco.
  • No podemos escribir simplemente head=$(head -c 1)porque si el primer carácter es una nueva línea, la sustitución de comandos eliminaría la nueva línea final, haciendo imposible distinguir entre la entrada vacía y la entrada que comienza con una línea en blanco.
  • En bash, ksh o zsh, puede reemplazar catpor </dev/stdinuna ganancia de rendimiento microscópico.

Si no le importa almacenar todos los datos intermedios en la memoria, aquí hay una implementación muy simple de pipe_if_not_empty.

pipe_if_not_empty () {
  input=$(cat; echo a);
  if [ "x$input" != x"a" ]; then
    { printf %s "${input%a}"; } | "$@"
  fi
}

Aquí hay una implementación un poco más simple con las siguientes advertencias:

  • Los datos producidos por la fuente se consideran vacíos si y solo si consisten únicamente en caracteres de nueva línea. (De hecho, esto puede ser deseable).
  • Los datos introducidos en el receptor terminan con exactamente un carácter de nueva línea, sin importar cuántas líneas nuevas terminen los datos producidos por la fuente. (Esto podría ser un problema).

Nuevamente, toda la información se almacena en la memoria.

pipe_if_not_empty () {
  input=$(cat);
  if [ "x$input" != x"" ]; then
    { printf '%s\n' "${input}"; } | "$@"
  fi
}
Gilles 'SO- deja de ser malvado'
fuente
17

Esto debería funcionar para ti

$ --a function-- | [ xargs -r ] --another function--

Un ejemplo

$ echo -e "\n\n" | xargs -r ls
$ # No output. ls did not run.
$ echo -e "\n\n1" | xargs -r ls
ls: cannot access 1: No such file or directory

Es simple pero debería funcionar para usted. Si su "una función" envía una cadena vacía o incluso una nueva línea por la tubería, xargs -r, evitará el paso a "otra función".

Referencia para xargs: http://www.oreillynet.com/linux/cmd/cmd.csp?path=x/xargs

-r, --no-run-if-empty
Do not run command if standard input contains only blanks.
Randy Skretka
fuente
9

ifne (1) de moreutils hace exactamente eso. Moreutils está disponible como paquete al menos en Debian y Ubuntu, probablemente también en otras distribuciones.

Jukka Matilainen
fuente
4

La siguiente función intenta leer el primer byte, y si tiene éxito, ese byte y los gatos se graban el resto. Debe ser eficiente y 100% portátil.

if_read() {
    IFS="" read -rN 1 BYTE && { echo -nE "$BYTE"; cat; } | "$@";
}

Casos de prueba:

$ echo -n | if_read wc -c
$ echo | if_read wc -c
1
$ echo -en "\nX" | if_read wc -c
2
$
Benjamin Polak
fuente
Esta función me falla cuando corro echo -en "\nX" | pipe_if_not_empty mail -s "Subject line here" [email protected]. Piensa eso liney hereson ambos destinatarios del correo electrónico, no tokens en el asunto. Tengo que escapar del "entorno del tema para que funcione. Sin embargo, la pipe_if_not_emptyfunción de la respuesta aceptada funciona para mí incluso sin escapar de nada.
kuzzooroo 05 de
2

Al menos algo como esto funciona:

yourcommand | if [ $(wc -c) -gt "0" ]; then yourothercommand; fi

Tenga en cuenta que lo anterior considerará los saltos de línea y otros caracteres especiales como salida, por lo que una línea vacía que se pasa a esa instrucción if se considerará como una salida. Simplemente aumente el límite de -gt si su salida debería ser superior a 1 byte :)

Janne Pikkarainen
fuente
yourothercommandnunca ve la salida de yourcommand.
Pausado hasta nuevo aviso.
2

En lugar de sender | receiver:

tester () { local a=$(</dev/stdin); if [[ $a ]]; then printf '%s\n' "$a" | receiver; fi; }
sender | tester

O podría hacerlo más general cambiándolo para aceptar el programa receptor como un argumento como en la respuesta de Gilles:

tester () { local a=$(</dev/stdin); if [[ $a ]]; then printf '%s\n' "$a" | "$@"; fi; }
sender | tester receiver
Pausado hasta nuevo aviso.
fuente