Как использовать sed для манипулирования непрерывно потоковым выводом?

13

Я составляю презентацию для нетехнической аудитории. У меня есть программа, работающая на Bash, которая выводит непрерывный поток значений, некоторые из которых важны. Я хотел бы выделить важные результаты, поскольку они отображаются, чтобы аудитория могла понять их частоту. Проблема в том, что я не могу sedработать на работающем потоке. Это работает нормально, если я положил результаты в файл, как в:

cat output.txt | sed "s/some text/some text bolded/"

Но если я попробую то же самое на работающем выводе, вот так:

command | sed "s/some text/some text bolded/"

sedничего не делает. Есть предположения?

Поскольку Ламберт был достаточно полезен, чтобы указать, мое высказывание, которое sedничего не делает, было расплывчатым. Происходит то, что программа выводит stdout(я почти уверен, что она не пишет stderr), как обычно, даже если она по каналу sed.

Кажется, проблема в том, что команда вызывает вторую программу, которая затем выводит на стандартный вывод. Есть несколько строк, напечатанных первой программой; это я могу редактировать. Затем идет поток значений, напечатанный второй программой; это я не могу редактировать.

Методы Perl и awk тоже не работают.

П Джонс
источник
5
Работает ли stdbuf -o0 command | sed "s/some text/some text bolded/"?
FloHimself
3
С 'sed ничего не делает' вы имеете в виду, что замена не сделана, или у вас нет вывода? Команда может писать в stderr вместо stdout? Если вы хотите выделить то, что вы могли бы использоватьcommand|egrep 'some text|$'
Ламберт
Команда записывает в stdout (а не в stderr)?
Антон
1
Учитывая, что текст появляется более одного раза, вы должны добавить gполученную «глобальную» подстановку, в противном случае будет подставлено только первое вхождение в строке:sed "s/old/new/g"
ph0t0nix
@ ph0t0nix Нет, это не проблема, но это было бы проблемой в будущем. Спасибо за чаевые.
П Джонс

Ответы:

12

Скорее всего, вывод команды буферизуется. Когда команда записывает в терминал, буфер сбрасывается на каждой новой строке, поэтому вы видите, что он появляется с ожидаемой скоростью. Когда команда записывает в канал, буфер очищается только тогда, когда он достигает нескольких килобайт, поэтому он сильно отстает. Таким образом, это стандартное поведение стандартной библиотеки ввода / вывода.

Чтобы заставить команду не буферизовать свой вывод, вы можете использовать unbuffer(из ожидаемого) или stdbuf(из GNU coreutils).

unbuffer command | sed …
stdbuf -o0 command | sed …
Жиль "ТАК - прекрати быть злым"
источник
stdbufне работал (это упоминалось ранее, кстати), но unbufferсделал !! Ты понятия не имеешь, как счастлив, что сделал меня.
П Джонс
И спасибо за краткое объяснение. Очень полезно.
П Джонс
sedсам по себе использует такой буфер (см. статью ChennyStar), поэтому приведенные здесь примеры могут не сработать, так как они sedявляются commandбуфером: cat /etc/passwd | unbuffer sedно sedсам по себе имеет -uопцию, поэтому grepможет быть более подходящим в этих примерах. Большое спасибо за вашу справочную информацию! Отличный ответ!
Математика
4

sed есть вариант для этого:

-u, --unbuffered

Который загружает минимальные объемы данных из входных файлов и чаще очищает выходные буферы. Смотрите man sedдля более подробной информации.

ChennyStar
источник
1
sedТолько GNU (не BSD sed), и я считаю, что это все равно не помешает буферизации команды в начале канала. Но приятно упомянуть об этом. :)
Wildcard
Страница man stdbuf гласит: «Если COMMAND настроит буферизацию своих стандартных потоков (например,« tee »), то это отменит соответствующие изменения с помощью stdbuf». Похоже, что sed может попасть в эту категорию, но флаг '-u' сделает его полезным в небуферизованной цепочке каналов.
MattSmith
2

Я бы использовал awk

  command | awk '/some important stuff/ { printf "%c[31m%s%c[0m\n",27,$0,27 ; next }
  { print ; } '

где

  • /some important stuff/ выберите важную строку, как в седе
  • printf "%c[31m%s%c[0m\n",27,$0,27 ; напечатать красным
    • используйте 32,33 для зеленого, желтого ...
    • $ 1, $ 2, можно использовать для выбора определенного поля
  • другая строка просто печатается «как есть»

Ключевым моментом является то, что commandследует очищать линии, но это должно быть в случае, если у вас много выходных данных.

Archemar
источник
Примечание: вопрос не говорит о том, что вся строка должна быть «выделена жирным шрифтом», а просто «какой-то текст». Хотя возможно реализовать и эту функцию в awk(используя sub()или gsub()), в случае такой примитивной замены sed, безусловно, подходящий инструмент.
Янис
1

Perl путь:

command | perl -pe 's/(stuff)/\x1b[1m$1\x1b[0m/g'

или с непрерывным выходом:

Скрипт bash для вывода cont:

#!/bin/bash
while [ true ]
do
   echo "Some stuff"
done

Тест с:

./cont | perl -pe 's/(stuff)/\x1b[1m${1}\x1b[0m/g'
  • \x1b[1m - жирный или повышенной интенсивности
  • ${1} - обратная связь
  • \x1b[0m - сбросить все атрибуты

Выход:

Некоторые вещи
Некоторые вещи
Некоторые вещи
Некоторые вещи

Больше кодов выхода здесь .

AB
источник