LINUX.ORG.RU

Перенаправить stderr и stdout в подпрограммы

 , , , ,


0

2

В соседнем треде мне посоветовали, как Разделить stderr и stdout. Спасибо vodz, который предложил следующую функцию:

filter() {
  local l n
  while read -r l; do
    n=${l#\[ninja\]}
    [ "x$l" != "x$n" ] && printf "%s\n" "$n" || printf "%s\n" "$l" >&2
  done
}
А теперь я хочу разделить stderr и stdout в разные файлы, напечатав в stdout по одному символу в счет каждой строки в соответствующем файле.

Т.е. для файла «log» со следующим содержанием:

[ninja][72/154] Building C object src/CMakeFiles/git2internal.dir/oid.c.o
[ninja][73/154] Building C object src/CMakeFiles/git2internal.dir/parse.c.o
[ninja][74/154] Building C object src/CMakeFiles/git2internal.dir/patch.c.o
[ninja][75/154] Building C object src/CMakeFiles/git2internal.dir/pack.c.o
../src/pack.c:790:22: warning: incompatible pointer types assigning to 'mz_alloc_func' (aka 'void *(*)(void *, unsigned long, unsigned long)') from 'void *(void *, unsigned int, unsigned int)' [-Wincompatible-pointer-types]
        obj->zstream.zalloc = use_git_alloc;
                            ^ ~~~~~~~~~~~~~
../src/pack.c:868:16: warning: incompatible pointer types assigning to 'mz_alloc_func' (aka 'void *(*)(void *, unsigned long, unsigned long)') from 'void *(void *, unsigned int, unsigned int)' [-Wincompatible-pointer-types]
        stream.zalloc = use_git_alloc;
                      ^ ~~~~~~~~~~~~~
2 warnings generated.
[ninja][76/154] Building C object src/CMakeFiles/git2internal.dir/posix.c.o
[ninja][77/154] Building C object src/CMakeFiles/git2internal.dir/patch_generate.c.o
[ninja][78/154] Building C object src/CMakeFiles/git2internal.dir/pqueue.c.o
[ninja][79/154] Building C object src/CMakeFiles/git2internal.dir/patch_parse.c.o
после пропускания через фильтр и обработки особым образом должно в stdout.txt быть:
[72/154] Building C object src/CMakeFiles/git2internal.dir/oid.c.o
[73/154] Building C object src/CMakeFiles/git2internal.dir/parse.c.o
[74/154] Building C object src/CMakeFiles/git2internal.dir/patch.c.o
[75/154] Building C object src/CMakeFiles/git2internal.dir/pack.c.o
[76/154] Building C object src/CMakeFiles/git2internal.dir/posix.c.o
[77/154] Building C object src/CMakeFiles/git2internal.dir/patch_generate.c.o
[78/154] Building C object src/CMakeFiles/git2internal.dir/pqueue.c.o
[79/154] Building C object src/CMakeFiles/git2internal.dir/patch_parse.c.o
а в stderr.txt следующее:
../src/pack.c:790:22: warning: incompatible pointer types assigning to 'mz_alloc_func' (aka 'void *(*)(void *, unsigned long, unsigned long)') from 'void *(void *, unsigned int, unsigned int)' [-Wincompatible-pointer-types]
        obj->zstream.zalloc = use_git_alloc;
                            ^ ~~~~~~~~~~~~~
../src/pack.c:868:16: warning: incompatible pointer types assigning to 'mz_alloc_func' (aka 'void *(*)(void *, unsigned long, unsigned long)') from 'void *(void *, unsigned int, unsigned int)' [-Wincompatible-pointer-types]
        stream.zalloc = use_git_alloc;
                      ^ ~~~~~~~~~~~~~
2 warnings generated.
на терминал при этом печатается:
....xxxxxxx....

Как это реализовано сейчас (внимание, говнокод, эстетам не смотреть):

process_out() {
	while read line; do
		echo "${line}" >> stdout.txt
		/bin/echo -n "."
	done < fifoout
}

process_err() {
	while read line; do
		echo "${line}" >> stderr.txt
		/bin/echo -n "x"
	done < fifoerr
}

mkfifo fifoout
mkfifo fifoerr
process_out &
process_err &
cat log | filter >fifoout 2>fifoerr
sleep 0.1
Почему сделано через FIFO? Важно, чтобы этот код работал с dash и busybox ash. Возможно ли реализовать то же самое, только лучше?

★★★★★

Последнее исправление: CYB3R (всего исправлений: 2)

И в чём проблема печатать '.' и 'x' в filter при записи строчек в лог?

#!./dash
filter() {
  local l n IFS
  while read -r l; do
    n=${l#\[ninja\]}
    if [ "x$l" != "x$n" ]; then
        printf "%s\n" "$n" >&4
        echo -n .
    else
        printf "%s\n" "$l" >&2
        echo -n x
    fi
  done
}

exec 4> stdout.txt
filter < log 2> stderr.txt
echo

vodz ★★★★★
()
Последнее исправление: vodz (всего исправлений: 1)
Ответ на: комментарий от vodz

Проблема в том, что на самом деле у меня filter и перенаправление вывода в разных скриптах. Для простоты я написал cat log | filter, в действительности это вызов внешнего скрипта, который запускает NINJA_STATUS="[ninja][%f/%t] " ninja -Cbuild | filter и много всего ещё.

CYB3R ★★★★★
() автор топика

Допустим, что filter, уже идеален и трогать его нельзя. У тебя process_xxx занимаются подсчетом линий. Во-вторых, сам не видишь, что они одинаковы с точностью до метки ‘.’<=>‘x’ и ‘out’<=>‘err’. Продолжай рефакторинг.

anonymous
()
Ответ на: комментарий от CYB3R

Проблема в том

Пока не вижу проблемы. Если у вас filter - отдельный скрипт, то просто функция не нужна, код становится ещё проще.

vodz ★★★★★
()
Ответ на: комментарий от CYB3R

Первую часть не сделал: зачем ты читаешь в память строки, если тебя совсем не интересует их содержимое? Может просто их посчитать (в однеричной системе счисления)? Еще подсказка: считывать можно концы строк.

anonymous
()
Ответ на: комментарий от vodz

Функция filter мне была нужна для разделения вывода ninja на stdout и stderr. Она – часть сборочного скрипта, у каждого пакета в дистрибутиве есть такой скрипт (да, логично вынести её наружу в какое-то общее для всех скриптов место, если таких пакетов >1). Второй скрипт обходит все пакеты и запускает их сборочные скрипты, он смотрит только на stdout, stderr и exit code от каждого сборочного скрипта, работает со всеми одинаково, независимо от того make там, ninja или что-то ещё. Если не уменьшать вывод в stdout и stderr в одну строку, вывод скрипта составляет более 10000 строк, что не очень удобно читать, потому все сохраняется в соответствующие файлы, а на консоль печатаются только точки и иксы.

CYB3R ★★★★★
() автор топика
Ответ на: комментарий от CYB3R

Пиши сразу в конечный файл, заодно от fifo избавишься.

Если что, конечного решения у меня нет. Возможно я ошибаюсь. По мне твоя задача должна решаться вполне читаемым однострочником не длинее 80 символов.

anonymous
()
Ответ на: комментарий от anonymous

При этом чувствуешь, что эти

посчитать EOL "stdout.log"

можно натравить на логи отдельно от основной логики.

anonymous
()
Ответ на: комментарий от anonymous

Не уверен, что это правильный подход и что это сильно отличается от текущей реализации.

CYB3R ★★★★★
() автор топика
Ответ на: комментарий от CYB3R

Не уверен, что это правильный подход

Это правильный подход. Потому что строки не будут грузиться и обрабатываться шеллом. Например, забудешь ключ -r для read. Или забудешь кавычки в echo $line и шелл перепарсит пробельные или спец символы, испортив оригинальную строку до неузнаваемости. Или столкнешься с Разделить stderr и stdout (комментарий): с echo домашнее задание напечатать строку «-n». Кстати что за изварт c /bin/echo -n ".", используй printf.

Строки посчитать/показать можно, например, так (набросок)

tail -f |  tr -d -c '\n' |  tr '\n' .

Можешь столкнуться с отсутвием интерактивности из-за буферизации пайпов, будет пачками выводить точки.

anonymous
()
Ответ на: комментарий от anonymous

Сейчас сделал это без FIFO:

process() {
  while read line; do
    echo "${line}" >> "std$1.txt"
    /bin/echo -n "$2"
  done
}

{ cat log | filter 2>&3 | process out .; } 3>&1 1>&2 | process err x

Вопрос только в том, чей exit code будет получен от такой конструкции.

CYB3R ★★★★★
() автор топика
Ответ на: комментарий от CYB3R

Ты не излечим.

    echo ...
    /bin/echo ... # что это? зачем? почему не printf?
# допустим, прочитали такую строку
line="-n как вывести '-n' в начале строки?"
echo $line

Поэтому не стоит грузить строку в баш, если ничего не собираешься делать с этой строкой. Очень высока вероятность, что ты не выведешь строку неизменной. Особенно, если ты знаешь баш на все свои 5 звезд.

anonymous
()
Ответ на: комментарий от anonymous

Ради портабельности могу использовать printf. В системе, на которой тестирую, built-in echo выводит все свои параметры на stdout, а /bin/echo умеет в параметры (-n, -e). Потому такой странный код. Не особо представляю, как можно это переписать без read, чтобы не огрести дополнительных проблем. И да, ABS я когда-то давно осилил, он у меня даже в печатном виде где-то есть, но на пять звезд я bash не знаю из-за отсутствия практики, которое я прямо сейчас восполняю.

CYB3R ★★★★★
() автор топика
Ответ на: комментарий от CYB3R

`/bin/echo` вполне может быть враппером над встроенным `echo`. Используй `printf`.

ABS я когда-то давно осилил

Я без понятия что такое ABS. Но если это как-то относится к теме, то ты врядли его осилил, потому что у тебя ноль остаточных знаний, судя по тому как ты успешно ходишь по граблям

anonymous
()
Ответ на: комментарий от anonymous
process() {
  while read line; do
    printf "%s\n" "${line}" >> "std$1.txt"
    printf "$2"
  done
}

Хорошо?

ABS. Да, остаточных знаний нет, потому что ничего на шелле так и не стал писать после прочтения. Но ЛОР действительно помогает, за два треда у меня уже есть две реально работающих функции. До этого треда моё решение с FIFO было уродским, теперь стало гораздо лучше, хоть ты и не доволен. Придется еще один создавать, чтобы мне подсказали, как получить из этой конструкции правильный exit code.

CYB3R ★★★★★
() автор топика
Ответ на: комментарий от CYB3R

https://www.tldp.org/LDP/abs/abs-guide.pdf

Ты прочитал 1000 страниц и у тебя не осталось остаточных знаний?

как получить из этой конструкции правильный exit code.

Судя по тому, что тебе понадобилось неделя, чтобы осилить printf (осилил ли?), ты не излечим. Ищи ответ в своей 1000-страничной книжке.

anonymous
()
Ответ на: комментарий от anonymous

Какие-то знания, конечно, остались, ведь как-то я пишу скрипты сейчас, но в книге описана куча разных нюансов, которые нужно учитывать при написании скриптов и о которых я и не вспомню. Иногда использую shellcheck, но все равно выходит говнокод.

CYB3R ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.