可以通过 shell 重定向捕获彩色输出吗?

我使用的各种 bash 命令——花哨的差异、构建脚本等等,都会产生大量的颜色输出。

当我将这个输出重定向到一个文件,然后再将 catless文件重定向到该文件时,颜色化就消失了——可能 b/c 重定向输出的行为剥离了告诉终端改变颜色的颜色代码。

是否有一种方法来捕获彩色输出,包括彩色化?

39541 次浏览

One way to capture colorized output is with the script command. Running script will start a bash session where all of the raw output is captured to a file (named typescript by default).

Redirecting doesn't strip colors, but many commands will detect when they are sending output to a terminal, and will not produce colors by default if not. For example, on Linux ls --color=auto (which is aliased to plain ls in a lot of places) will not produce color codes if outputting to a pipe or file, but ls --color will. Many other tools have similar override flags to get them to save colorized output to a file, but it's all specific to the individual tool.

Even once you have the color codes in a file, to see them you need to use a tool that leaves them intact. less has a -r flag to show file data in "raw" mode; this displays color codes. edit: Slightly newer versions also have a -R flag which is specifically aware of color codes and displays them properly, with better support for things like line wrapping/trimming than raw mode because less can tell which things are control codes and which are actually characters going to the screen.

some programs remove colorization when they realize the output is not a TTY (i.e. when you redirect them into another program). You can tell some of those to use color forcefully, and tell the pager to turn on colorization, for example use less -R

I found that using script to preserve colors when piping to less doesn't really work (less is all messed up and on exit, bash is all messed up) because less is interactive. script seems to really mess up input coming from stdin even after exiting.

So instead of running:

script -q /dev/null cargo build | less -R

I redirect /dev/null to it before piping to less:

script -q /dev/null cargo build < /dev/null | less -R

So now script doesn't mess with stdin and gets me exactly what I want. It's the equivalent of command | less but it preserves colors while also continuing to read new content appended to the file (other methods I tried wouldn't do that).

I use tee: pipe the command's output to teefilename and it'll keep the colour. And if you don't want to see the output on the screen (which is what tee is for: showing and redirecting output at the same time) then just send the output of tee to /dev/null:

command| teefilename> /dev/null

Inspired by the other answers, I started using script. I had to use -c to get it working though. All other answers, including tee, different script examples did not work for me.

Context:

  • Ubuntu 16.04
  • running behavior tests with behave and starting shell command during the test with python's subprocess.check_call()

Solution:

script --flush --quiet --return /tmp/ansible-output.txt --command "my-ansible-command"

Explanation for the switches:

  • --flush was needed, because otherwise the output is not well live-observable, coming in big chunks
  • --quiet supresses the own output of the script tool
  • -c, --command directly provides the command to execute, piping from my command to script did not work for me (no colors)
  • --return to make script propagate the exit code of my command so I know if my command has failed

This question over on superuser helped me when my other answer (involving tee) didn't work. It involves using unbuffer to make the command think it's running from a shell.

I installed it using sudo apt install expect tcl rather than sudo apt-get install expect-dev.

I needed to use this method when redirecting the output of apt, ironically.