it-swarm-eu.dev

Vypněte ukládání do vyrovnávací paměti v potrubí

Mám skript, který volá dva příkazy:

long_running_command | print_progress

The long_running_command tiskne pokrok, ale jsem z toho nespokojen. Používám print_progress aby to bylo hezčí (jmenovitě vytisknu postup do jednoho řádku).

Problém: Připojení potrubí ke stdout také aktivuje 4K buffer, takže Nice print program nedostane nic ... nic ... nic ... celek hodně ... :)

Jak mohu zakázat 4K buffer pro long_running_command (ne, nemám zdroj)?

409
Aaron Digulla

Můžete použít příkaz unbuffer (který je součástí balíčku expect ), např.

unbuffer long_running_command | print_progress

unbuffer se připojí k long_running_command přes pseudoterminál (pty), což způsobuje, že systém s ním zachází jako s interaktivním procesem, a proto nepoužívá vyrovnávací paměť 4 kiB v potrubí, což je pravděpodobná příčina zpoždění.

U delších potrubí může být nutné zrušit vyrovnávací paměť každého příkazu (kromě posledního), např.

unbuffer x | unbuffer -p y | z
262
cheduardo

Dalším způsobem, jak tuto kočku stáhnout, je použít program stdbuf , který je součástí Coreutils GNU Coreutils (FreeBSD má také svůj vlastní)).

stdbuf -i0 -o0 -e0 command

Tím se zcela vypne ukládání do vyrovnávací paměti pro vstup, výstup a chyby. Pro některé aplikace může být z důvodů výkonu vhodnější ukládání do vyrovnávací paměti:

stdbuf -oL -eL command

Všimněte si, že funguje pouze pro vyrovnávací paměť stdio (printf(), fputs()...) pro dynamicky propojené aplikace a pouze v případě, že tato aplikace jinak neupravuje vyrovnávací paměť svých standardních toků samotných, i když by to mělo zahrnovat většinu aplikací.

474
a3nm

Ještě další způsob, jak zapnout výstupní režim vyrovnávací paměti řádku pro long_running_command je použití příkazu script, který spustí váš long_running_command v pseudo terminálu (pty).

script -q /dev/null long_running_command | print_progress      # FreeBSD, Mac OS X
script -c "long_running_command" /dev/null | print_progress    # Linux
78
chad

Pro grep, sed a awk můžete vynutit, aby byl výstup vyrovnáván na řádku. Můžeš použít:

grep --line-buffered

Vynutit výstup do vyrovnávací paměti řádku. Ve výchozím nastavení je výstup ukládán do vyrovnávací paměti, pokud je standardní výstup terminál a blok je ukládán do vyrovnávací paměti.

sed -u

Nastavit vyrovnávací paměť na výstupním řádku.

Více informací naleznete na této stránce: http://www.perkin.org.uk/posts/how-to-fix-stdio-buffering.html

69
yaneku

Pokud je problém s libcem modifikujícím jeho buffering/flushing, když výstup nejde na terminál, měli byste zkusit socat . Můžete vytvořit obousměrný tok mezi téměř jakýmkoli druhem I/O mechanismu. Jedním z nich je rozeklaný program mluvící s pseudotty.

 socat EXEC:long_running_command,pty,ctty STDIO 

To, co dělá, je

  • vytvořit pseudo tty
  • vidlice long_running_command s otrokovou stranou Pty jako stdin/stdout
  • vytvořit obousměrný tok mezi hlavní stranou Pty a druhou adresou (zde je STDIO)

Pokud vám dává stejný výstup jako long_running_command, pak můžete pokračovat s dýmkou.

Edit: Wow Neviděl unbuffer odpověď! Socat je stejně skvělý nástroj, takže bych mohl tuto odpověď nechat

52
shodanex

Můžeš použít

long_running_command 1>&2 |& print_progress

Problém je v tom, že libc bude line-buffer při stdout na obrazovku a full-buffer při stdout do souboru. Ale ne-buffer pro stderr.

Nemyslím si, že je to problém s potrubním bufferem, je to všechno o zásadách libc pro buffer.

20
Wang HongQin

Býval to případ a pravděpodobně stále platí, že když je standardní výstup zapsán na terminál, je ve výchozím nastavení vyrovnávací paměť řádku - když je zapsán nový řádek, je linka zapsána na terminál. Když je standardní výstup odeslán do potrubí, je plně vyrovnávací paměť - takže data jsou odeslána do dalšího procesu v potrubí, když je vyplněna standardní vyrovnávací paměť I/O.

To je zdroj problémů. Nejsem si jistý, zda je toho mnoho, co můžete udělat, abyste to napravili, aniž byste upravili zápis programu do kanálu. Můžete použít funkci setvbuf() s příznakem _IOLBF A bezpodmínečně uvést stdout do režimu vyrovnávací paměti řádku. Ale nevidím snadný způsob, jak to vynutit v programu. Nebo program umí fflush() ve vhodných bodech (po každém řádku výstupu), ale platí stejný komentář.

Předpokládám, že pokud jste nahradili potrubí za pseudo-terminál, pak by standardní I/O knihovna myslela, že výstup byl terminál (protože je to typ terminálu) a automaticky by vyrovnávací paměť linky. To je složitý způsob, jak se s věcmi vypořádat.

11
Jonathan Leffler

Vím, že se jedná o starou otázku, která již měla spoustu odpovědí, ale pokud se chcete vyhnout problému s vyrovnávací pamětí, zkuste něco jako:

stdbuf -oL tail -f /var/log/messages | tee -a /home/your_user_here/logs.txt

Výsledkem budou protokoly v reálném čase a také jejich uložení do logs.txt soubor a vyrovnávací paměť již nebudou mít vliv na tail -f příkaz.

7
Marin Nedea

Nemyslím si, že problém je s trubkou. Zní to, že váš dlouhodobě probíhající proces dostatečně často nevypláchne svůj vlastní buffer. Změna velikosti vyrovnávací paměti potrubí by byla hackem, který by ji obešel, ale nemyslím si, že je to možné bez přestavby jádra - něco, co byste nechtěli dělat jako hack, protože to pravděpodobně ovlivňuje mnoho dalších procesů.

5
anon

Podobně jako odpověď Čada , můžete napsat malý skript, jako je tento:

# save as ~/bin/scriptee, or so
script -q /dev/null sh -c 'exec cat > /dev/null'

Potom použijte tento příkaz scriptee jako náhradu za tee.

my-long-running-command | scriptee

Bohužel, nemůžu vypadat, že by taková verze fungovala perfektně v Linuxu, takže se zdá být omezena na unixy ve stylu BSD.

V operačním systému Linux je to blízko, ale výzvu nedostanete zpět, až skončí (dokud nestisknete klávesu Enter atd.) ...

script -q -c 'cat > /proc/self/fd/1' /dev/null
3
jwd

Podle tento příspěvek zde , můžete zkusit zmenšit ulimit potrubí na jeden jediný blok 512 bajtů. Určitě to nevypne ukládání do vyrovnávací paměti, ale dobře, 512 bajtů je mnohem méně než 4K: 3

2
RAKK

Našel jsem toto chytré řešení: (echo -e "cmd 1\ncmd 2" && cat) | ./Shell_executable

To dělá trik. cat přečte další vstup (až do EOF) a předá jej do potrubí poté, co echo vloží své argumenty do vstupního proudu Shell_executable.

1
jaggedsoft

Podle this je velikost vyrovnávací paměti potrubí nastavena v jádře a bude vyžadovat, abyste změnili jádro.

0
second