Supposons que vous ayez un pipeline comme celui-ci:
$ a | b
Si b
arrête le traitement stdin, après un certain temps, le tuyau se remplit et écrit, de a
à sa sortie standard, se bloquera (jusqu'à ce que le b
traitement recommence ou qu'il meure).
Si je voulais éviter cela, je pourrais être tenté d'utiliser un tuyau plus gros (ou, plus simplement buffer(1)
) comme ceci:
$ a | buffer | b
Cela me ferait simplement gagner plus de temps, mais a
finirait par s'arrêter.
Ce que j'aimerais avoir (pour un scénario très spécifique que j'aborde), c'est d'avoir un tuyau "qui fuit" qui, une fois plein, laisserait tomber certaines données (idéalement, ligne par ligne) du tampon pour laisser a
continuer traitement (comme vous pouvez probablement l'imaginer, les données qui circulent dans le tuyau sont extensibles, c'est-à-dire que le traitement des données b
est moins important que de a
pouvoir fonctionner sans blocage).
Pour résumer, j'aimerais avoir quelque chose comme un tampon délimité et qui fuit:
$ a | leakybuffer | b
Je pourrais probablement l'implémenter assez facilement dans n'importe quelle langue, je me demandais juste s'il y avait quelque chose de "prêt à l'emploi" (ou quelque chose comme un bash one-liner) qui me manque.
Remarque: dans les exemples, j'utilise des tuyaux normaux, mais la question s'applique également aux tuyaux nommés
Bien que j'aie attribué la réponse ci-dessous, j'ai également décidé d'implémenter la commande leakybuffer car la solution simple ci-dessous avait certaines limites: https://github.com/CAFxX/leakybuffer
Réponses:
Le moyen le plus simple serait de passer par un programme qui définit une sortie non bloquante. Voici un simple perl oneliner (que vous pouvez enregistrer en tant que leakybuffer ) qui le fait:
donc votre
a | b
devient:ce qui est fait est de lire l'entrée et d'écrire sur la sortie (comme
cat(1)
) mais la sortie n'est pas bloquante - ce qui signifie que si l'écriture échoue, elle renverra une erreur et perdra des données, mais le processus se poursuivra avec la ligne d'entrée suivante car nous ignorons commodément le Erreur. Le processus est en quelque sorte mis en mémoire tampon comme vous le souhaitez, mais voir la mise en garde ci-dessous.vous pouvez tester avec par exemple:
vous obtiendrez un
output
fichier avec des lignes perdues (la sortie exacte dépend de la vitesse de votre shell, etc.) comme ceci:vous voyez où le shell a perdu des lignes après
12773
, mais aussi une anomalie - le perl n'avait pas assez de tampon pour12774\n
mais il l'a fait1277
donc il a écrit juste cela - et donc le numéro suivant75610
ne commence pas au début de la ligne, ce qui le rend peu laid.Cela pourrait être amélioré en ayant perl détecter quand l'écriture n'a pas complètement réussi, puis essayer plus tard de vider le reste de la ligne tout en ignorant les nouvelles lignes qui arrivent, mais cela compliquerait beaucoup plus le script perl, donc est laissé comme exercice pour le lecteur intéressé :)
Mise à jour (pour les fichiers binaires): Si vous ne traitez pas les lignes terminées par la nouvelle ligne (comme les fichiers journaux ou similaires), vous devez modifier légèrement la commande, ou perl consommera de grandes quantités de mémoire (selon la fréquence à laquelle les caractères de nouvelle ligne apparaissent dans votre entrée):
cela fonctionnera également correctement pour les fichiers binaires (sans consommer de mémoire supplémentaire).
Update2 - sortie de fichier texte plus agréable: éviter les tampons de sortie (
syswrite
au lieu deprint
):semble résoudre les problèmes avec les "lignes fusionnées" pour moi:
(Remarque: on peut vérifier sur quelles lignes la sortie a été coupée avec:
perl -ne '$c++; next if $c==$_; print "$c $_"; $c=$_' output
oneliner)la source
perl -w -MFcntl -e 'fcntl STDOUT,F_SETFL,O_WRONLY|O_NONBLOCK; while (<STDIN>) { print }' | aplay -t raw -f dat --buffer-size=16000
, Perl semble allouer continuellement plus de mémoire jusqu'à ce qu'il soit tué par le gestionnaire OOM.dd
dedd oflag=nonblock status=none
.$| = 1
et votresyswrite()
approche empêche donc les écritures courtes tant que les lignes sont raisonnablement courtes.