我们的 RHEL 7 机器有很长的日志文件,我询问了切入的缓冲这问题。这个问题仍然存在,但一些实验显示了不同的问题。
我决定尝试使用按字节切割,而不是按字符切割,并发现输出缓冲在一台机器上不同,但在另一台机器上则不然:
在一台机器上,两个循环:
for ((ii=0;ii<5;ii++)); do date; usleep 500000 ; done | cut -b 1-99
for ((ii=0;ii<5;ii++)); do date; usleep 500000 ; done | cut -c 1-99
(观察-c
vs -b
for cut
)随着循环的进行,两者都显示日期五次。
在另一台机器上,这个循环按字节进行切割:
for ((ii=0;ii<5;ii++)); do date; usleep 500000 ; done | cut -b 1-99
显示循环进行时的时间:
for ((ii=0;ii<5;ii++)); do date; usleep 500000 ; done | cut -c 1-99
保持输出直到循环完成。如果我将其设置为永远运行,它会显示一组时间,每 8192 字节的输出。正如预期的那样,每秒两次,但输出被缓冲。
两个问题,
- 为什么一个系统与另一个系统不同?
- 为什么 cut 的两种用法的输出缓冲不同?
答案1
这不是标准输出实际上,这次不是缓冲。 (标准输出缓冲的默认设置是仅将行缓冲区输出发送到终端。)
首先,这不是上游 coreutils 功能,并且您在 Debian 等系统中看不到该问题。无论手册页和输出--help
说什么,实际的上游代码都认为-c
是-b
相同的,请参见例如:https://github.com/coreutils/coreutils/blob/v9.1/src/cut.c#L483
然而,有一个国际化补丁,coreutils-i18n
它提供了对基于区域设置的多字节字符的支持,并且红帽似乎携带了该补丁。
该补丁还提供了一个单独的输入缓冲宏,用于cut -c
此处:
+/* Refill the buffer BUF to get a multibyte character. */
+#define REFILL_BUFFER(BUF, BUFPOS, BUFLEN, STREAM) \
+ do \
+ { \
+ if (BUFLEN < MB_LEN_MAX && !feof (STREAM) && !ferror (STREAM)) \
+ { \
+ memmove (BUF, BUFPOS, BUFLEN); \
+ BUFLEN += fread (BUF + BUFLEN, sizeof(char), BUFSIZ, STREAM); \
+ BUFPOS = BUF; \
+ } \
+ }
+ while (0)
这不是一个循环,但它fread()
会阻塞直到 EOF 或直到它有一个完整的缓冲区。在ltrace
(not )下运行程序显示它在我尝试过的 CentOS 系统上strace
阻塞。fread_unlocked()
您对此无能为力,实现告诉 stdio 它需要BUFLEN
字节,仅此而已。不,禁用输入缓冲没有帮助,因为它只会影响 stdio 提前读取的内容超过应用程序要求的内容。
i18n 补丁似乎也有其他问题,至少在过去,请参见例如https://lwn.net/Articles/535735/和https://bugzilla.redhat.com/show_bug.cgi?id=499220
如果您只有 ASCII 字符,则可以切换到,这与您在其他一些 Linux 系统上cut -b
使用的方法相同。cut -c
或者,切换到其他工具,例如perl -C -ne 'print substr($_, 0,99)'
.