问题描述
我正在研究一个脚本,以在后台测试新硬盘(以便我可以关闭终端窗口)并记录输出。我的问题是要让badblocks将stdout打印到日志文件,以便我可以监视其多日进度并创建格式正确的更新电子邮件。
我已经能够使用以下命令将stdout打印到日志文件:(标记为r / w,%monitor,详细)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""
通过解析控制字符(然后插入回车符,以便不被下一个终端行立即打印出来)来打印预期/期望的结果,但使用{ {1}}或cat sdx.log > some.file
都仍包含所有多余的字符(尽管在电子邮件中它们被解释为空格)。这种解决方案(或类似的解决方案在访问时会解码或删除控制字符)仍然会产生巨大的不必要的输出文件。
我已经通过以下类似的问题进行了工作,但是没有一个(至少我可以弄清楚)产生一种解决方案,该解决方案可以与输出实时地更新文件,而不是要求保存的日志文件是在任务完成写入后单独处理,或者直到该过程完成后才写入日志文件,这两者都违反了监视进度的既定目标。
Bash - process backspace control character when redirecting output to file
How to "apply" backspace characters within a text file (ideally in vim)
谢谢!
解决方法
我在现实生活中遇到的主要问题是尝试处理手册页。过去,我一直使用一个简单的脚本,通过适当地去除退格键来发布进程。也许可以用80个字符的perl来做这种事情,但这是一种很好地处理backspace和cr / nl的方法。我没有进行广泛的测试,但是对于简单的情况,它会产生良好的输出。例如:
$ 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.
如果您的输出开始有很多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 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,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,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;
}
,
您可以删除git checkout .
^H