Supongamos que tiene una tubería como la siguiente:
$ a | b
Si b
deja de procesar la entrada estándar, después de un tiempo la tubería se llena y escribe desde a
su entrada estándar, se bloqueará (hasta que b
comience a procesar nuevamente o muera).
Si quisiera evitar esto, podría sentir la tentación de usar una tubería más grande (o, más simplemente buffer(1)
) de esta manera:
$ a | buffer | b
Esto simplemente me daría más tiempo, pero al final a
finalmente se detendría.
Lo que me encantaría tener (para un escenario muy específico que estoy abordando) es tener una tubería "con fugas" que, cuando está llena, dejaría caer algunos datos (idealmente, línea por línea) desde el búfer para permitir a
continuar procesamiento (como probablemente pueda imaginar, los datos que fluyen en la tubería son prescindibles, es decir, tener los datos procesados b
es menos importante que a
poder ejecutarlos sin bloquearlos).
Para resumir, me encantaría tener algo así como un búfer limitado y con fugas:
$ a | leakybuffer | b
Probablemente podría implementarlo con bastante facilidad en cualquier idioma, me preguntaba si hay algo "listo para usar" (o algo así como un bash one-liner) que me estoy perdiendo.
Nota: en los ejemplos estoy usando tuberías regulares, pero la pregunta se aplica igualmente a las tuberías con nombre
Si bien obtuve la respuesta a continuación, también decidí implementar el comando leakybuffer porque la solución simple a continuación tenía algunas limitaciones: https://github.com/CAFxX/leakybuffer
Respuestas:
La forma más fácil sería canalizar a través de algún programa que establezca la salida sin bloqueo. Aquí hay un simple perl oneliner (que puede guardar como leakybuffer ) que lo hace:
entonces tu se
a | b
convierte en:lo que hace es leer la entrada y escribir en la salida (igual que
cat(1)
) pero la salida no se bloquea, lo que significa que si la escritura falla, devolverá un error y perderá datos, pero el proceso continuará con la siguiente línea de entrada, ya que ignoramos convenientemente error. El proceso tiene el tipo de línea de búfer que desea, pero vea la advertencia a continuación.puedes probar con, por ejemplo:
obtendrá un
output
archivo con líneas perdidas (el resultado exacto depende de la velocidad de su shell, etc.) de esta manera:puedes ver dónde perdió el shell líneas después
12773
, pero también una anomalía: el perl no tenía suficiente búfer,12774\n
pero sí1277
lo hizo, así que escribió exactamente eso, por lo que el siguiente número75610
no comienza al comienzo de la línea, por lo que es poco feo.Eso podría mejorarse haciendo que Perl detecte cuándo la escritura no tuvo éxito por completo, y luego intente eliminar el resto de la línea mientras ignora las nuevas líneas entrantes, pero eso complicaría mucho más el guión de Perl, por lo que se deja como un ejercicio para el lector interesado :)
Actualización (para archivos binarios): si no está procesando líneas terminadas en nueva línea (como archivos de registro o similares), debe cambiar ligeramente el comando, o Perl consumirá grandes cantidades de memoria (dependiendo de la frecuencia con la que aparecen caracteres de nueva línea en su entrada):
funcionará correctamente también para archivos binarios (sin consumir memoria adicional).
Actualización2: salida de archivo de texto más agradable: evitar búferes de salida (en
syswrite
lugar deprint
):parece solucionar problemas con "líneas combinadas" para mí:
(Nota: se puede verificar en qué líneas se cortó la salida con:
perl -ne '$c++; next if $c==$_; print "$c $_"; $c=$_' output
oneliner)fuente
perl -w -MFcntl -e 'fcntl STDOUT,F_SETFL,O_WRONLY|O_NONBLOCK; while (<STDIN>) { print }' | aplay -t raw -f dat --buffer-size=16000
, Perl parece asignar continuamente más memoria hasta que el administrador de OOM lo mata.dd
'sdd oflag=nonblock status=none
.$| = 1
susyswrite()
enfoque evita escrituras cortas siempre que las líneas sean razonablemente cortas.