Как мне использовать тройник для перенаправления на 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

Печатается только первая строка, и больше ничего! Могу ли я использовать tee для перенаправления на grep, или я делаю это неправильно?

Когда я набирал этот вопрос, вторая команда фактически сработала один раз для меня, я запустил ее снова пять раз, а затем вернулся к результату в одну строку. Это только моя система? (Я запускаю Zsh в Tmux).

И наконец, почему с первой командой «grep syslog» не отображается как результат (результат только один)?

Для контроля здесь grep без 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

Обновление: кажется, что head заставляет целую команду обрезаться (как указано в ответе ниже), теперь приведенная ниже команда возвращает следующее:

ps aux | tee >(grep syslog) | head -n1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
syslog     806
Rqomey
источник
Не прямой ответ на ваш вопрос, но было бы намного чище просто сделать что-то подобное 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дважды: один раз в стандартный вывод и один раз в аргумент. Это означает, что один из этих пунктов назначения получит данные в первую очередь.

    Однако трубы все забуферены. Вполне вероятно, что эти буферы занимают 1 строку каждый, но они могут быть больше, что может привести к тому, что одна из принимающих команд увидит все, что ей нужно для вывода (т. grepЕ. Строку ped), до того, как другая команда ( 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получает первую строку ввода, а затем закрывает свой стандартный ввод и завершает работу. Когда teeвидит, что его стандартный вывод был закрыт, он закрывает собственный стандартный вывод (вывод из ps) и завершает работу. Это может зависеть от реализации.

По сути, единственными данными, которые psможно отправить, является первая строка (определенно, потому что headона контролирует это) и, возможно, некоторые другие строки перед head& teeзакрытием их дескрипторов stdin.

Несоответствие тому, появляется ли вторая строка, связано с синхронизацией: headзакрывает стандартный ввод, но psвсе еще отправляет данные. Эти два события не очень хорошо синхронизированы, поэтому строка, содержащая их, syslogвсе еще имеет шанс сделать ее teeаргументом ( grepкомандой). Это похоже на объяснения выше.

Вы можете полностью избежать этой проблемы, используя команды, которые ждут весь ввод перед закрытием stdin / exiting. Например, используйте 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
Вы можете использовать то, что не закрывает стандартный ввод вместо head. Я обновил ответ на этом примере:ps aux | tee >(grep syslog) | awk 'NR == 1'
mrb
1
@KrzysztofAdamski, когда вы используете >(cmd), оболочка создает именованный канал и передает его в качестве аргумента команде ( tee). Затем teeпишет в стандартный вывод (по каналу awk), а также в этот аргумент. Это так же, как mkfifo a_fifo ; grep ... a_fifoв одной оболочке и ps | tee a_fifo | awk ...в другой.
mrb
1
@KrzysztofAdamski gnu.org/software/bash/manual/html_node/... - Try echo >(exit 0), который отзовется фактический аргумент , передаваемый оболочкой (в моем случае, это будет /dev/fd/63). Это должно работать одинаково на bash и zsh.
mrb
1
@mrb: это очень интересная функция, которую я раньше не знал, спасибо. В bash он работает странным образом, однако, смотрите pastebin.com/xFgRcJdF . К сожалению, у меня нет времени, чтобы исследовать это сейчас, но я сделаю это завтра.
Кшиштоф Адамски
2

grep syslogне всегда отображается, так как это зависит от времени. При использовании оболочки конвейера вы выполняете команды практически одновременно. Но ключевым здесь является слово «почти». Если psсканирование всех процессов завершается до запуска grep, его нет в списке. Вы можете получить случайные результаты в зависимости от загрузки системы и т. Д.

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

Что касается вопроса, это поведение довольно странно. Это потому, что он не используется в обычном режиме. Он запускается без каких-либо аргументов, что означает, что он должен просто копировать данные из своего стандартного ввода в стандартный вывод. Но его стандартный вывод перенаправляется на запущенную головку subshell (в первом случае) или grep (во втором случае). Но это также передается следующей команде. Я думаю, что то, что происходит в этом случае, на самом деле зависит от реализации. Например, на моем bash 4.2.28 ничего не пишется в subshell stdin. На zsh он работает надежно так, как вы хотели бы (печатая как первую строку ps, так и искомые строки), каждый раз, когда я пытаюсь,

Кшиштоф Адамски
источник
В любом случае это объясняет одну вещь, я удивлен, что тройник задерживает запуск grep в заметной степени!
Rqomey
0

Немного хакерски, но вот мое решение в виде psgrep()функции оболочки, которую я использую:

Перенаправьте psстроку заголовка STDERR, затем grepвключите STDOUT, но сначала удалите grepсаму команду, чтобы избежать появления строки «noise» grep:

psgrep() { ps aux | tee >(head -1>&2) | grep -v " grep $@" | grep "$@" -i --color=auto; }
фн
источник