为什么 cut -c 的标准输出行缓冲与 cut -b 不同?

为什么 cut -c 的标准输出行缓冲与 cut -b 不同?

我们的 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

(观察-cvs -bfor 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 字节的输出。正如预期的那样,每秒两次,但输出被缓冲。

两个问题,

  1. 为什么一个系统与另一个系统不同?
  2. 为什么 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)'.

相关内容