Як використовувати трійник для перенаправлення на grep


13

Я не маю великого досвіду використання трійника, тому сподіваюся, що це не дуже просто.

Переглянувши одну з відповідей на це питання, я зіткнувся з дивним поведінкою tee.

Для того, щоб я вивів перший рядок і знайдений рядок, я можу використовувати це:

ps aux | tee >(head -n1) | grep syslog
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
syslog     806  0.0  0.0  34600   824 ?        Sl   Sep07   0:00 rsyslogd -c4

Однак, коли я вперше запустив це (в zsh), результат був у неправильному порядку, заголовки стовпців були нижче результатів grep (однак це не повторилося), тому я спробував поміняти команди навколо:

ps aux | tee >(grep syslog) | head -n1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND

Друкується лише перший рядок, і більше нічого! Чи можу я використовувати трійник для переадресації на grep, чи роблю це неправильно?

Коли я набирав це питання, друга команда насправді працювала один раз для мене, я запускав її ще п'ять разів, а потім повертався до результату в один рядок. Це лише моя система? (Я запускаю zsh в межах tmux).

Нарешті, чому з першою командою "grep syslog" не відображається як результат (є лише один результат)?

Для контролю тут використовується греп без tee

ps aux | grep syslog
syslog     806  0.0  0.0  34600   824 ?        Sl   Sep07   0:00 rsyslogd -c4
henry    2290  0.0  0.1  95220  3092 ?        Ssl  Sep07   3:12 /usr/bin/pulseaudio --start --log-target=syslog
henry   15924  0.0  0.0   3128   824 pts/4    S+   13:44   0:00 grep syslog

Оновлення: Здається, що головна команда змушує усієї команди скорочувати (як зазначено у відповіді нижче) команда нижче повертає наступне:

ps aux | tee >(grep syslog) | head -n1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
syslog     806

Це не пряма відповідь на ваше запитання, але було б набагато чіткіше просто зробити щось подібне ps aux | sed -n -e '1p' -e '/syslog/p'.
jw013

Я ніколи навіть не думав про sed, я думаю, що це може бути підходящою відповіддю на відповідне питання тут, але я фактично шукаю інформацію про непослідовну поведінку цих команд!
Rqomey

Відповіді:


19
$ ps aux | tee >(head -n1) | grep syslog
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND 
syslog     806  0.0  0.0  34600   824 ?        Sl   Sep07   0:00 rsyslogd -c4

Команди grepта headкоманди починаються приблизно в один і той же час, і обидва отримують однакові вхідні дані під час власного дозвілля, але загалом, коли дані стають доступними. Деякі речі можуть ввести "несинхронізований" вихід, який перевертає лінії; наприклад:

  1. Мультиплексовані дані teeфактично надсилаються в один процес перед іншим, в першу чергу залежно від реалізації tee. Проста teeреалізація дасть readдеяку кількість вводу, а потім writeдвічі: один раз для stdout і один раз для його аргументу. Це означає, що одне з цих напрямків отримає дані першими.

    Однак труби буферні. Ймовірно, що ці буфери мають по 1 рядку кожен, але вони можуть бути більшими, що може призвести до того, що одна з команд прийому побачить усе, що потрібно для виведення (тобто grepрядок пед), перш ніж інша команда ( head) отримає будь-які дані на всі.

  2. Незважаючи на вищезазначене, також можливо, що одна з цих команд отримує дані, але не в змозі нічого з цим зробити вчасно, а потім інша команда отримує більше даних і швидко обробляє їх.

    Наприклад, навіть якщо headі grepнадсилаються дані по одному рядку, якщо headвін не знає, як з цим боротися (або затримується з плануванням ядра), він grepможе показати його результати, перш ніж headнавіть отримає шанс. Щоб продемонструвати, спробуйте додати затримку: ps aux | tee >(sleep 1; head -n1) | grep syslogЦе майже напевно отримає grepвихід перший.

$ ps aux | tee >(grep syslog) | head -n1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND

Я вважаю, що ви часто отримуєте тут лише один рядок, тому що headотримує перший рядок введення, а потім закриває його stdin та виходить. Коли teeбачить, що його stdout закритий, він закриває власний stdin (вихід з ps) і виходить. Це може залежати від впровадження.

Фактично, єдині дані, які psотримує надсилання - це перший рядок (безумовно, тому, що headце контролює), а може бути і деякі інші рядки перед head& teeзакриттям їхніх дескрипторів stdin.

Невідповідність того, чи з’являється другий рядок, вводиться тимчасовим часом: headзакриває stdin, але psвсе ще надсилає дані. Ці два події недостатньо синхронізовані, тому рядок, що містить, syslogвсе ще має шанс перейти teeдо аргументу s ( grepкоманда). Це схоже на пояснення вище.

Ви можете повністю уникнути цієї проблеми, скориставшись командами, які очікують на весь вхід до закриття stdin / exit. Наприклад, використовуйте awkзамість head, який буде читати та обробляти всі його рядки (навіть якщо вони не викликають виводу):

ps aux | tee >(grep syslog) | awk 'NR == 1'

Але зауважте, що рядки все ще можуть виглядати поза порядком, як зазначено вище, що можна продемонструвати:

ps aux | tee >(grep syslog) | (sleep 1; awk 'NR == 1')

Сподіваюся, це було не надто багато деталей, але є багато одночасних речей, що взаємодіють між собою. Окремі процеси працюють одночасно без будь-якої синхронізації, тому їх дії на будь-якому конкретному виконанні можуть змінюватися; іноді це допомагає заглибитися в основні процеси, щоб пояснити, чому.


1
Відмінна відповідь! Я насправді запитав, бо мене цікавлять основні процеси. Коли речі непостійні, мені це цікаво. Чи був би кращий спосіб запустити, ps aux | tee >(grep syslog) | head -n1який зупинив би headзакриття стрибуту. Нічого собі, ця команда почала давати вихід вже зараз, але як це станеться відповідно до вашої відповіді, вона, здається, урізанаUSER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND syslog 806
Rqomey

1
Ви можете використовувати щось, що не закриває stdin замість head. Я оновив відповідь на цьому прикладі:ps aux | tee >(grep syslog) | awk 'NR == 1'
mrb

1
@KrzysztofAdamski, коли ви використовуєте >(cmd), оболонка створює іменовану трубку і передає це як аргумент команді ( tee). Тоді teeпише в stdout (на трубу awk), а також до цього аргументу. Це те саме, що і mkfifo a_fifo ; grep ... a_fifoв одній оболонці, і ps | tee a_fifo | awk ...в іншій.
мрб

1
@KrzysztofAdamski gnu.org/software/bash/manual/html_node/… - Спробуйте echo >(exit 0), який повторить фактичний аргумент, переданий оболонкою (у моєму випадку це стає /dev/fd/63). Це має працювати так само на bash та zsh.
mrb

1
@mrb: це дуже цікава особливість, про яку я раніше не знав, дякую. Це працює дивним чином у bash, однак, дивіться pastebin.com/xFgRcJdF . На жаль, у мене немає часу досліджувати це зараз, але я це зроблю завтра.
Кшиштоф Адамський

2

grep syslogне завжди показано, оскільки це залежить від термінів. Використовуючи оболонку конвеєра, ви виконуєте команди майже одночасно. Але ключовим тут є слово «майже». Якщо psсканування всіх процесів до запуску grep, воно не буде в списку. Ви можете отримати випадкові результати залежно від навантаження системи тощо.

Подібне відбувається і з твоїм трійником. Він запускається на задньому плані в фоновому режимі, і він може бути запущений до або після grep. Ось чому порядок виводу є непослідовним.

Що стосується трійкового питання, то його поведінка досить дивна. Це тому, що він не використовується нормально. Він запускається без жодних аргументів, а це означає, що він повинен просто копіювати дані з його stdin в stdout. Але його stdout переспрямовується на нижню частину запущеної головки (у першому випадку) або grep (2-й випадок). Але це також передається до наступної команди. Я думаю, що те, що відбувається в даному випадку, насправді залежить від реалізації. Наприклад, на моєму базі 4.2.28, ніколи нічого не написано, щоб знизити stdin. На zsh, він працює надійно так, як ви хочете (друкуючи як перший рядок ps, так і пошукові рядки) щоразу, коли я намагаюся,


Це так чи інакше пояснює, я здивований, що трійник затримує затримку роботи на помітній мірі!
Rqomey

0

Трохи хакі, але ось моє рішення, у вигляді psgrep()функції оболонки, яку я використовую:

Перенаправляйте psрядок заголовка на STDERR, потім grepна STDOUT, але спочатку видаліть саму grepкоманду, щоб уникнути "шумового" рядка, що випливає з grepсебе:

psgrep() { ps aux | tee >(head -1>&2) | grep -v " grep $@" | grep "$@" -i --color=auto; }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.