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



我正在编写一个脚本,在后台测试新的硬盘驱动器(这样我就可以关闭终端窗口(并记录输出。我的问题是让Badblock将stdout打印到日志文件中,这样我就可以监控它的多日进度并创建格式正确的更新电子邮件。

我已经能够用以下内容将stdout打印到日志文件中:(标志为r/w、%monitor、verbose(
sudo badblocks -b 4096 -wsv /dev/sdx 2>&1 | tee sdx.log

通常情况下,输出看起来像:
Testing with pattern 0xaa: 2.23% done, 7:00 elapsed. (0/0/0 errors)

不使用换行符,^H控制命令将备份光标,然后新更新的状态将覆盖以前的状态。

不幸的是,控制字符没有被处理,而是被保存为文件中的一个字符,产生上面的输出,然后是^H的43个副本、新更新的统计数据、^H的43个拷贝等。

由于输出每秒至少更新一次,因此会生成比所需文件大得多的文件,并且很难检索当前状态。

在终端中工作时,解决方案cat sdx.log && echo""通过解析控制字符(然后插入回车,这样它就不会立即被下一个终端行打印出来(来打印预期/想要的结果,但使用cat sdx.log > some.filecat sdx.log | mail仍然包括所有额外的字符(尽管在电子邮件中它们被解释为空格(。这种解决方案(或类似的解决方案,在访问时解码或删除控制字符(仍然会产生巨大的、不必要的输出文件

我已经解决了以下类似的问题,但没有一个解决方案(至少我能想出(与输出实时工作以更新文件,而是要求在任务完成后单独处理保存的日志文件,或者在处理完成之前不写入日志文件,这两者都违背了监测进展的既定目标。

Bash-将输出重定向到文件时处理退格控制字符

如何";应用";文本文件中的退格字符(最好在vim中(

谢谢!

我在现实生活中遇到的主要问题是处理手册页。在过去,我总是使用一个简单的脚本,通过适当地去掉退格来进行后期处理。在80个字符的perl中可能可以做这种事情,但这里有一种方法可以很好地处理退格和cr/nl。我没有进行过广泛的测试,但它为简单的案例提供了良好的输出。例如:

$ printf 'xxxrabclxborhelnworldn' | ./a.out output
hello
world
$ cat output
hello
world
$ xxd output
00000000: 6865 6c6c 6f0a 776f 726c 640a            hello.world.

如果你的输出开始有很多csi序列,这种方法就不值得麻烦了。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 usagen");
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

我发现col-b和colcrt很有用,但没有一个对我来说完美。这些将应用控制字符,而不仅仅是丢弃它们:

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

最新更新