¿Hay una manera de canalizar la salida de una función a otra, pero sólo si no es una salida?
¿Hay una manera de canalizar la salida de una función a otra, pero sólo si no es una salida?
Respuestas:
La siguiente ifnotempty
funció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 --foo
en sink --bar
por 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:
dd
no leer más del byte que se le dice que lea en su entrada estándar.head -c 1
sería un reemplazo adecuado para dd bs=1 count=1 2>/dev/null
Linux.head -n 1
no sería adecuado porque head
normalmente 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 head
e incluso read -r -n 1 head
no son adecuados aquí porque si el primer carácter es una nueva línea, head
se 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.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.cat
por </dev/stdin
una 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:
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
}
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.
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
$
echo -en "\nX" | pipe_if_not_empty mail -s "Subject line here" foo@bar.com
. Piensa eso line
y here
son 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_empty
función de la respuesta aceptada funciona para mí incluso sin escapar de nada.
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 :)
yourothercommand
nunca ve la salida de yourcommand
.
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