3

把标准输出伪装成终端

 2 years ago
source link: https://blog.lilydjwg.me/2013/7/9/pretend-that-stdout-is-a-tty.39922.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

把标准输出伪装成终端

本文来自依云's Blog,转载请注明。

fcitx-diagnose 是 fcitx 输入法的非常优秀的诊断脚本。当输出到终端时,fcitx-diagnose 会给输出加上易于区分不同类型的消息的彩色高亮。可是,当用户把输出重定向到文件以便让其他人帮助查看时,这些高亮就没了。fcitx-diagnose 的输出很长,但如果通过管道给 less 查看的看,这些彩色也会消失。

要是 fcitx-diagnose 支持--color=always这样的选项就好了。可是 yyc 说他懒得写。getopt我只在 C 里用过,好麻烦的,所以我也懒得写。于是,我还是用我的 ptyless 好了。后来又想到,用于改变 I/O 缓冲方式的 unbuffer 和 stdbuf 应该也可以。测试结果表明,只有 unbuffer 可行,因为它是和 ptyless 一样使用伪终端的。stdbuf 则是使用 LD_PRELOAD 载入一个动态链接库的方式来设置缓冲区。

不过,既然 stdbuf 用 LD_PRELOAD 来设置缓冲区,我何不来用相同的办法改变isatty()函数的返回值呢?同时,我也学学 stdbuf,试了下__attribute__ ((constructor))指令。

libstdoutisatty.c
#include<stdarg.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<dlfcn.h>
static int (*orig_isatty)(int) = 0;
int isatty(int fd){
if(fd == 1){
return 1;
}
return orig_isatty(fd);
}
void die(char *fmt, ...) {
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fprintf(stderr, "\n");
fflush(stderr);
exit(-1);
}
__attribute__ ((constructor)) static void setup(void) {
void *libhdl;
char *dlerr;
if (!(libhdl=dlopen("libc.so.6", RTLD_LAZY)))
die("Failed to patch library calls: %s", dlerror());
orig_isatty = dlsym(libhdl, "isatty");
if ((dlerr=dlerror()) != NULL)
die("Failed to patch isatty() library call: %s", dlerr);
}

然后,像 stdbuf、proxychains 那样做了个包装,不用自己手动设置 LD_PRELOAD 环境变量了。这也是我第一次使用 CMake,比 GNU 的 autotools 那套简单多了 :-)

使用方法很简单:

  1. 克隆或者下载源码
  2. 编译之
    $ mkdir -p build && cd build
    $ cmake .. # 或者安装到 /usr 下: cmake .. -DCMAKE_INSTALL_PREFIX=/usr
    $ make
  3. 安装之
    $ sudo make install
    $ sudo ldconfig
  4. 可以使用了:
    $ stdoutisatty fcitx-diagnose | less

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK