Перенаправление stdout игнорирует строки без новой строки

Перенаправление stdout игнорирует строки без новой строки

Я пытаюсь сделать так, чтобы мой stderrbe печатался красным в терминале. Скрипт ниже перенаправляет 2на пользовательскую 8ловушку отладки.

exec 9>&2
exec 8> >(
    while IFS='' read -r line || [ -n "$line" ]; do
       echo -e "${RED}${line}${COLORRESET}"
    done
)
function undirect(){ exec 2>&9; } # reset to original 9 (==2)
function redirect(){ exec 2>&8; } # set to custom 8
trap "redirect;" DEBUG
PROMPT_COMMAND='undirect;'

Оно произошло отздесь, с понятным объяснением.

Кажется, работает очень хорошо, однако ввод, не завершающийся новой строкой, вообще не выводится. Цитата автораевангелияснова:

bash> echo -en "hi\n" 1>&2
    hi       <-- this is red
bash> echo -en "hi" 1>&2
bash> echo -en "hi" 1>&2
bash> echo -en "hi\n" 1>&2
    hihihi   <-- this is red

Я не могу понять, почему. Содержимое, не являющееся новой строкой, похоже, попадает в какой-то буфер. Оно либо даже не достигает файлового дескриптора 8, либо каким-то образом не хочет сразу выводиться на печать. Куда оно попадает? redirectвызывается правильно каждый раз. Также, IFS=''означает, что нет разделителя, поэтому я не совсем понимаю, почему эхо в in 8происходит построчно.

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

Все это решение, как указал Жиль, не совсем идеально. У меня проблемы с чтением, stdin, индикаторами выполнения, не могу ни то, ни suдругое source. И часто возникают серьезные проблемы, такие как сломанные каналы и неожиданные выходы терминала. Если кто-то попал сюда по моей ссылке, пожалуйста, рассмотрите возможность использованияhttps://github.com/sickill/stderredвместо этого, он стал намного лучше (пока проблем нет) (однако echo bla >&2остается не красным исоответствующий вопрос закрыт)

решение1

Вы получили частичные строки вывода, как часть той же строки в точке, где была напечатана новая строка. Части строки буферизуются внутри read,вот что он делает:

Theчитатьутилита должна считывать одну логическую строку из стандартного ввода

Например, это печатается <foobar>через одну секунду, а не <foo><bar>.

(echo -n foo ; sleep 1 ; echo bar) | (read x ; echo "<$x>")

Если вы хотите перехватывать вводимые данные частями, меньшими, чем полные строки, вам нужно сделать что-то еще, например, с помощью Perl. Это выведет <foo><bar\n>(с новой строкой перед последним >, поскольку в отличие от read, Perl не обрабатывает последнюю новую строку специально. Не должно иметь значения при раскрашивании.)

(echo -n foo ; sleep 1 ; echo bar) | 
    perl -e '$|=1; while(sysread STDIN,$a,9999) { print "<$a>"}'

Если у вас есть экспортированные в среду коды управления цветами ( REDи ), вы можете использовать их из скрипта Perl, как здесь:COLORRESET

perl -e '$|=1; while(sysread STDIN,$a,9999) {print "$ENV{RED}$a$ENV{COLORRESET}"}'

решение2

В Bash вы можете использовать -dопцию встроенной команды read, которая определяет символ конца строки. man bashона гласит следующее:

-d delim    The first character of delim is used to terminate the input line, 
            rather than newline.

Если он не определен, readждет \nпоявления, чтобы считать строку строкой. Но когда вы используете опцию -d, вы можете установить NULв качестве разделителя. Конечно, вам также нужно завершить ввод NUL.

Пример:

printf "%s\0" $'x\n' y z | while IFS='' read -r -d $'\0' line
    do
        printf "%s\n" "$line"
    done

Выход:

x

y
z

Еще раз, но теперь printfв курсе whileсобытий нет \n.

printf "%s\0" $'x\n' y z | while IFS='' read -r -d $'\0' line
    do
        printf "%s" "$line"
    done

Выход:

x
yz(...)

Я добавил (...)и это означает, что в конце второй строки нет End-Of-Line. Но текст все равно обрабатывается и печатается.

Связанный контент