简体   繁体   English

在将 sdout 记录到文件时实时处理退格控制字符 (^H)

[英]Processing backspace control character (^H) in real time while logging sdout to file

I am working on a script to test new-to-me hard drives in the background (so I can close the terminal window) and log the outputs.我正在编写一个脚本来在后台测试新的硬盘驱动器(这样我就可以关闭终端窗口)并记录输出。 My problem is in getting badblocks to print stdout to the log file so I can monitor its multi-day progress and create properly formatted update emails.我的问题是让 badblocks 将标准输出打印到日志文件,这样我就可以监控它的多天进度并创建格式正确的更新电子邮件。

I have been able to print stdout to a log file with the following: (flags are r/w, % monitor, verbose)我已经能够使用以下内容将 stdout 打印到日志文件中:(标志为 r/w、% monitor、verbose)
sudo badblocks -b 4096 -wsv /dev/sdx 2>&1 | tee sdx.log

Normally the output would look like:通常 output 看起来像:
Testing with pattern 0xaa: 2.23% done, 7:00 elapsed. (0/0/0 errors)

No new-line character is used, the ^H control command backs up the cursor, and then the new updated status overwrites the previous status.不使用换行符,^H控制命令备份cursor,然后新更新的状态覆盖之前的状态。

Unfortunately, the control character is not processed but saved as a character in the file, producing the above output followed by 43 copies of ^H , the new updated stats, 43 copies of ^H , etc.不幸的是,控制字符没有被处理,而是作为一个字符保存在文件中,生成上面的 output 后跟 43 个^H副本,新更新的统计数据,43 个^H副本,等等。

Since the output is updated at least once per second, this produces a much larger file than necessary, and makes it difficult to retrieve the current status.由于 output 每秒至少更新一次,因此生成的文件比所需的大得多,并且难以检索当前状态。

While working in terminal, the solution cat sdx.log && echo"" prints the expected/wanted results by parsing the control characters (and then inserting a carriage return so it is not immediately printed over by the next terminal line), but using cat sdx.log > some.file or cat sdx.log | mail在终端中工作时,解决方案cat sdx.log && echo""通过解析控制字符(然后插入一个回车符,这样它就不会立即被下一个终端行打印出来)来打印预期/想要的结果,但是使用cat sdx.log > some.filecat sdx.log | mail cat sdx.log | mail both still include all of the extra characters (though in email they are interpreted as spaces). cat sdx.log | mail仍然包含所有额外字符(尽管在 email 中它们被解释为空格)。 This solution (or ones like it which decode or remove the control character at the time of access still produce a huge, unnecessary output file.这个解决方案(或类似的在访问时解码或删除控制字符的解决方案)仍然会产生一个巨大的、不必要的 output 文件。

I have worked my way through the following similar questions, but none have produced (at least that I can figure out) a solution which works in real time with the output to update the file, instead requiring that the saved log file be processed separately after the task has finished writing, or that the log file not be written until the process is done, both of which defeat the stated goal of monitoring progress.我已经解决了以下类似的问题,但没有一个产生(至少我能弄清楚)一个与 output 实时更新文件的解决方案,而不是要求在之后单独处理保存的日志文件任务已经完成写入,或者直到进程完成才写入日志文件,这两者都违背了监视进度的既定目标。

Bash - process backspace control character when redirecting output to file Bash - 将 output 重定向到文件时处理退格控制字符

How to "apply" backspace characters within a text file (ideally in vim) 如何在文本文件中“应用”退格字符(最好在 vim 中)

Thank you!谢谢你!

The main place I've run into this in real life is trying to process man pages.我在现实生活中遇到的主要问题是尝试处理手册页。 In the past, I've always used a simple script that post processes by stripping out the backspace appropriately.过去,我一直使用一个简单的脚本,通过适当地去除退格来发布进程。 One could probably do this sort of thing in 80 character of perl, but here's an approach that handles backspace and cr/nl fairly well.人们可能可以在 80 个字符的 perl 中做这种事情,但这里有一种方法可以很好地处理退格和 cr/nl。 I've not tested extensively, but it produces good output for simple cases.我没有进行广泛的测试,但它在简单的情况下产生了良好的输出。 eg:例如:

$ printf 'xxx\rabclx\bo\rhel\nworld\n' | ./a.out output
hello
world
$ cat output
hello
world
$ xxd output
00000000: 6865 6c6c 6f0a 776f 726c 640a            hello.world.

If your output starts to have a lot of csi sequences, this approach just isn't worth the trouble.如果您的输出开始有很多 csi 序列,这种方法就不值得麻烦了。 cat will produce nice human consumable output for those cases. cat将在这些情况下产生很好的人类消耗品输出。

#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

FILE * xfopen(const char *path, const char *mode);
off_t xftello(FILE *stream, const char *name);
void xfseeko(FILE *stream, off_t offset, int whence, const char *name);

int
main(int argc, char **argv)
{
        const char *mode = "w";
        char *name = strchr(argv[0], '/');
        off_t last = 0, max = 0, curr = 0;
        name = name ? name + 1 : argv[0];
        if( argc > 1 && ! strcmp(argv[1], "-a")) {
                argv += 1;
                argc -= 1;
                mode = "a";
        }
        if( argc > 1 && ! strcmp(argv[1], "-h")) {
                printf("usage: %s [-a] [-h] file [ file ...]\n", name);
                return EXIT_SUCCESS;
        }
        if( argc < 2 ) {
                fprintf(stderr, "Missing output file.  -h for usage\n");
                return EXIT_FAILURE;
        }
        assert( argc > 1 );
        argc -= 1;
        argv += 1;

        FILE *ofp[argc];
        for( int i = 0; i < argc; i++ ) {
                ofp[i] = xfopen(argv[i], mode);
        }
        int c;
        while( ( c = fgetc(stdin) ) != EOF ) {
                fputc(c, stdout);
                for( int i = 0; i < argc; i++ ) {
                        if( c == '\b' ) {
                                xfseeko(ofp[i], -1, SEEK_CUR, argv[i]);
                        } else if( isprint(c) ) {
                                fputc(c, ofp[i]);
                        } else if( c == '\n' ) {
                                xfseeko(ofp[i], max, SEEK_SET, argv[i]);
                                fputc(c, ofp[i]);
                                last = curr + 1;
                        } else if( c == '\r' ) {
                                xfseeko(ofp[i], last, SEEK_SET, argv[i]);
                        }
                }
                curr = xftello(ofp[0], argv[0]);
                if( curr > max ) {
                        max = curr;
                }
        }
        return 0;
}

off_t
xftello(FILE *stream, const char *name)
{
        off_t r = ftello(stream);
        if( r == -1 ) {
                perror(name);
                exit(EXIT_FAILURE);
        }
        return r;
}

void
xfseeko(FILE *stream, off_t offset, int whence, const char *name)
{
        if( fseeko(stream, offset, whence) ) {
                perror(name);
                exit(EXIT_FAILURE);
        }
}

FILE *
xfopen(const char *path, const char *mode)
{
        FILE *fp = fopen(path, mode);
        if( fp == NULL ) {
                perror(path);
                exit(EXIT_FAILURE);
        }
        return fp;
}

你可以删除^H

sudo badblocks -b 4096 -wsv /dev/sdx 2>&1 | tr -d '\b' | tee sdx.log

I have found col -b and colcrt usefull, but none worked perfect for me.我发现col -bcolcrt 很有用,但没有一个对我来说是完美的。 These will apply control characters, not just drop them:这些将应用控制字符,而不仅仅是删除它们:

sudo badblocks -b 4096 -wsv /dev/sdx 2>&1 | col -b | tee sdx.log

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM