Труби, як надходять дані в трубопровід?


22

Я не розумію, як дані надходять у конвеєр, і сподіваюся, що хтось міг би пояснити, що там відбувається.

Я думав, що конвеєр команд обробляє файли (текст, масиви рядків) по черзі. (Якщо кожна команда сама працює рядок.) Кожен рядок тексту проходить через конвеєр, команди не чекають, коли попередній закінчить обробку всього вводу.

Але, здається, це не так.

Ось тестовий приклад. Є деякі рядки тексту. Я прописую їх великими літерами і повторюю кожен рядок двічі. Я так роблю з cat text | tr '[:lower:]' '[:upper:]' | sed 'p'.

Щоб слідкувати за процесом, ми можемо запустити його "інтерактивно" - пропустимо ім'я вхідного файлу в cat. Кожна частина трубопроводу проходить по черзі:

$ cat | tr '[:lower:]' '[:upper:]'
alkjsd
ALKJSD
sdkj
SDKJ
$ cat | sed 'p'
line1
line1
line1
line 2
line 2
line 2

Але повний конвеєр дійсно чекає, коли я закінчуватиму вхід, EOFі лише потім друкує результат:

$ cat | tr '[:lower:]' '[:upper:]' | sed 'p'
I am writing...
keep writing...
now ctrl-D
I AM WRITING...
I AM WRITING...
KEEP WRITING...
KEEP WRITING...
NOW CTRL-D
NOW CTRL-D

Невже так? Чому це не по черзі?


Це не труба, вона catбуферизація, поки stdin не закриється.
золотинки

але trі sedвиконувати рядки технологічних процесів catдо
завершення

За замовчуванням, що використовується stdio (я вважаю, що всі згадані програми використовують), полягає в тому, що stderr є небуферизованим, а stdout буферизований рядком під час запису в термінал і повністю буферизований інакше (наприклад, якщо він записує у файл або трубку) . Деякі команди мають прапори, які можуть змінювати буферизацію stdout, але схоже, що tr це не робить.
kasperd

Відповіді:


36

Існує загальне правило буферизації, яке дотримується стандартна бібліотека вводу-виводу C ( stdio), якою користуються більшість програм Unix. Якщо вихід збирається до терміналу, він видається в кінці кожного рядка; інакше він змивається лише тоді, коли буфер (8K в моїй системі Linux / amd64; у вашій може бути різний) заповнений.

Якщо всі ваші утиліти слідували загальним правилом, ви побачите вихід з затримкою у всіх ваших прикладах ( cat|sed, cat|trі cat|tr|sed). Але є виняток: GNU catніколи не буферизує свій вихід. Він або не використовує, stdioабо змінює stdioполітику буферування за замовчуванням .

Я можу бути впевненим, що ви використовуєте GNU, catа не якийсь інший unix, catоскільки інші не поводитимуться таким чином. У традиційному unix catє -uможливість запитувати небуферний вихід. GNU catігнорує цю -uопцію, оскільки її вихід завжди не буферний.

Отже, коли у вас є труба з a catзліва, в системі GNU пропуск даних по трубі не затримується. catНавіть не збирається через підрядник - ваш термінал робить. Поки ви вводите інформацію для кота, ваш термінал знаходиться в "канонічному" режимі - на основі рядків, з ключами для редагування, такими як backspace та ctrl-U, що пропонує вам можливість редагувати рядок, який ви ввели, перш ніж надсилати його Enter.

У cat|tr|sedприкладі trвсе ще надходять дані, catяк тільки ви натискаєте Enter, але trдотримуєтесь stdioполітики за замовчуванням: його вихід збирається в трубу, тому він не спалахує після кожного рядка. Він записує у другу трубу, коли буфер заповнений або коли приймається EOF, залежно від того, що відбувається першим.

sedтакож дотримується stdioполітики за замовчуванням, але її вихід збирається до терміналу, тому він запише кожен рядок, як тільки закінчиться з ним. Це впливає на те, скільки потрібно ввести до того, як щось з’явиться на іншому кінці трубопроводу - якщо sedблокування його виводу блокувало, вам доведеться набрати вдвічі більше (щоб заповнити trвихідний буфер і sed вихід буфер).

У GNU sedє -uопція, тож якщо ви скасували порядок і використали, cat|sed -u|trви побачите, що вихід з'явиться миттєво знову. ( sed -uОпція може бути доступна в іншому місці, але я не думаю, що це давня традиція Unix на кшталт cat -u) Наскільки я можу сказати, немає еквівалентного варіанту tr.

Існує утиліта, stdbufяка називається, яка дозволяє змінювати режим буферизації будь-якої команди, яка використовує параметри stdioза замовчуванням. Це трохи крихко, оскільки він використовує LD_PRELOADдля досягнення чогось, що бібліотека C не була розроблена для підтримки, але в цьому випадку, здається, працює:

cat | stdbuf -o 0 tr '[:lower:]' '[:upper:]' | sed 'p'

1
Спасибі! Дивовижна відповідь. Напевно, я повинен якось згадати буферизацію у питанні, щоб можна було його знайти.
xealits

teeа ddтакож зазвичай грають за власними правилами. У поєднанні образно, три інструменти можуть досить портативно заперечувати будь-які потреби stdbufу фонових трубопроводах.
mikeserv

1
Це одна з причин уникати марного використання кота .
поварки

8

Це насправді вимагало розуміння і ще більше відповіді. Відмінне запитання (я його схвалюю далі).

Ви знехтували спробувати tr | sedсвої налагоджувальні елементи вище:

>tr '[:lower:]' '[:upper:]' | sed 'p'
i am writing
still writing
now ctrl-d
I AM WRITING
I AM WRITING
STILL WRITING
STILL WRITING
NOW CTRL-D
NOW CTRL-D
>

Так очевидно trбуфери. Дізнайтеся щось нове щодня!

Редагувати :

Як я вважаю це, ми виділили причину, але не надали пояснення. Якщо cat | trвін пише відразу ж, якщо ви cat | sed, він пише відразу, але якщо ви tr | sed, вона чекає за EOF. Я хотів би запропонувати відповідь може бути похований в trабо sedвихідний код , то і не бути проблемою труби.

Редагувати :

Я бачу, як Wumpus надав пояснення, коли я друкував останню зміну. Спасибі!


1
дійсно вони буферні! і тест з приблизно 8 кбітними лініями, як згадував Wumpus, показує, що буфер дійсно становить 8 Кб. Я хотів би прийняти обидві відповіді, щоб поділитися якоюсь репутацією, але я візьму Wumpus як більш повну. Все одно, дякую!
xealits

1
Немає проблем, моя була емпіричною відповіддю, його - знаючою.
Пуассон Аерохед

Дивіться також це питання, яке показує, як використовувати, stdbufщо також може бути корисним. unix.stackexchange.com/questions/182537/…
Джо
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.