标准输出的重定向忽略没有换行符的行

标准输出的重定向忽略没有换行符的行

我试图stderr在终端中将我的打印为红色。下面的脚本将重定向到调试陷阱时的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=''意味着没有分隔符,所以我不太明白为什么回显是按8行发生的。

错误修复将不胜感激,我将引用的答案链接到这个问题。

正如吉尔斯所指出的,整个解决方案并不十分完美。我在读取、标准输入、进度条方面遇到问题,既不能su也不能source。经常出现诸如管道破裂和意外的航站楼出口等重大问题。如果有人通过我的链接到达这里,请考虑使用https://github.com/sickill/stderred相反,它要好得多(还没有问题)(但echo bla >&2仍然是非红色和相应的问题已关闭

答案1

您确实获得了部分行输出,作为打印换行符的同一行的一部分。该行的部分在 内缓冲read这就是它的作用

实用程序应从标准输入读取单个逻辑行

例如,<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

再说一次,但现在循环printfwhile不支持\n.

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

输出:

x
yz(...)

我添加了(...),这意味着第二行末尾没有行尾。但文本仍然会被处理和打印。

相关内容