Od stdout
manuálová stránka:
Stream stderr je bez vyrovnávací paměti. Stream stdout je ukládán do vyrovnávací paměti,když ukazuje na terminál .Částečné řádky se neobjeví, dokud nebude zavoláno fflush(3) nebo exit(3) nebo dokud nebude vytištěn nový řádek.
Sečteno a podtrženo:Pokud výstupem není terminál, váš program bude mít standardně standardní výstup v plně vyrovnávací paměti. To v podstatě znamená, že bude vypisovat data ve velkých blocích, nikoli po řádcích, natož po znaku.
Způsoby, jak to obejít:
-
Opravte svůj program:Pokud potřebujete výstup v reálném čase, musíte svůj program opravit. V C můžete použít
fflush(stdout)
za každým výstupním příkazem nebosetvbuf()
pro změnu režimu ukládání do vyrovnávací paměti standardního výstupu. Pro Python existujesys.stdout.flush()
dokonce i některé návrhy zde. -
Použijte nástroj, který umí nahrávat z PTY, spíše než přímo přesměrování stdout. GNU Screen to může udělat za vás:
screen -d -m -L python test.py
byl by začátek. Tím se zaznamená výstup vašeho programu do souboru s názvem
screenlog.0
(nebo podobně) ve vašem aktuálním adresáři s výchozím zpožděním 10 sekund a můžete použítscreen
pro připojení k relaci, kde běží váš příkaz, za účelem poskytnutí vstupu nebo jeho ukončení. Zpoždění a název souboru protokolu lze změnit v konfiguračním souboru nebo ručně, jakmile se připojíte k relaci na pozadí.
EDIT:
Na většině systémů Linux existuje třetí řešení:Můžete použít LD_PRELOAD
proměnná a předinstalovaná knihovna k přepsání vybraných funkcí knihovny C a jejich použití k nastavení stdout
režim ukládání do vyrovnávací paměti, když jsou tyto funkce volány vaším programem. Tato metoda může fungovat, ale má řadu nevýhod:
-
Na statických spustitelných souborech to nebude vůbec fungovat
-
Je křehký a spíše ošklivý.
-
Se spustitelnými soubory SUID to nebude fungovat vůbec - dynamický zavaděč odmítne číst
LD_PRELOAD
při načítání takových spustitelných souborů z bezpečnostních důvodů. -
Je křehký a spíše ošklivý.
-
Vyžaduje, abyste našli a přepsali funkci knihovny, která je volána vaším programem po zpočátku nastaví
stdout
režim ukládání do vyrovnávací paměti a nejlépe před jakýkoli výstup.getenv()
je dobrou volbou pro mnoho programů, ale ne pro všechny. Možná budete muset přepsat běžné I/O funkce, jako jeprintf()
nebofwrite()
- pokud push přijde na shove, možná budete muset přepsat všechny funkce, které řídí režim ukládání do vyrovnávací paměti a zavést speciální podmínku prostdout
. -
Je křehký a spíše ošklivý.
-
Je těžké zajistit, aby nedošlo k nežádoucím vedlejším účinkům. Chcete-li toto právo provést, musíte zajistit, aby pouze
stdout
je ovlivněna a že vaše přepsání nezhroutí zbytek programu, pokud např.stdout
je zavřeno. -
Zmínil jsem se, že je křehký a spíše ošklivý?
To znamená, že proces je relativně jednoduchý. Vložíte soubor C, např. linebufferedstdout.c
náhradní funkce:
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
char *getenv(const char *s) {
static char *(*getenv_real)(const char *s) = NULL;
if (getenv_real == NULL) {
getenv_real = dlsym(RTLD_NEXT, "getenv");
setlinebuf(stdout);
}
return getenv_real(s);
}
Potom tento soubor zkompilujete jako sdílený objekt:
gcc -O2 -o linebufferedstdout.so -fpic -shared linebufferedstdout.c -ldl -lc
Poté nastavte LD_PRELOAD
proměnnou, která ji načte spolu s vaším programem:
$ LD_PRELOAD=./linebufferedstdout.so python test.py | tee -a test.out
0
1000
2000
3000
4000
Pokud budete mít štěstí, váš problém bude vyřešen bez neštěstí vedlejší účinky.
Můžete nastavit LD_PRELOAD
knihovnu v shellu, je-li to nutné, nebo dokonce specifikujte tuto knihovnu v celém systému (rozhodně NE doporučeno) v /etc/ld.so.preload
.
Uvažovali jste o tom, že budete hrát odpaliště?
./program | tee a.txt
Nicméně ani tee nebude fungovat, pokud "program" nezapíše nic do stdout, dokud to není hotovo. Účinnost tedy hodně závisí na tom, jak se váš program chová.
Pokud se pokoušíte upravit chování existujícího programu, zkuste stdbuf (součást coreutils počínaje verzí 7.5 zřejmě).
Toto ukládá stdout až do řádku:
stdbuf -oL command > output
Toto zcela zakáže ukládání do vyrovnávací paměti stdout:
stdbuf -o0 command > output