121
ls -l --color=auto | tee output.log

Without pipe/tee it's colored. How can I make it so that it stays colored while using tee (can be colored only on the screen, I don't care about colors in logs).

Paweł Gościcki
  • 2,728
  • 3
  • 23
  • 26
  • [Already asked on Unix&Linux](http://unix.stackexchange.com/questions/10823/where-do-my-ansi-escape-codes-go-when-i-pipe-to-another-process-can-i-keep-them). – Dan Dascalescu Sep 15 '16 at 22:14
  • A possible solution: `script -efq -c "ls --color=auto" >(cat) | tee outpu.log` – nadapez May 10 '22 at 00:38

5 Answers5

166

Simply insert unbuffer before any command to make it think it is writing to an interactive output even if it is actually piping into another executable. This will preserve color in the case of ls.

For example

unbuffer ls -l --color=auto | tee output.log

If you don't already have it installed, on Ubuntu and other Debian-ish Linux distributions you can install unbuffer by doing.

sudo apt-get install expect-dev
Eamonn O'Brien-Strain
  • 1,776
  • 1
  • 11
  • 4
  • 10
    Another solution, which does not require installing anything, is at http://stackoverflow.com/questions/3515208/can-colorized-output-be-captured-via-shell-redirect – Tgr Jan 31 '15 at 02:51
  • 6
    This causes the resulting file to contain color codes (of course); is there any way to then *print* the file in a way that makes use of the color codes and properly displays the colors in the terminal? – Kyle Strand Jul 10 '15 at 18:48
  • 4
    Ugh, that makes password entries show your password in cleartext! – AndiDog Aug 20 '15 at 13:37
  • 2
    @Tgr That solution didn't work for me on OS X trying to get the raw colored output of `xcodebuild`— instead I got chopped-up lines with no color.  `unbuffer xcodebuild | less -R` worked flawlessly, however. – Slipp D. Thompson Nov 24 '15 at 05:33
  • Ugh, unbuffer tries to install `expect libdrm-amdgpu1 libdrm-intel1 libdrm-nouveau2 libdrm-radeon1 libdrm2 libfontenc1 libgl1-mesa-dri libgl1-mesa-glx libglapi-mesa libice6 libllvm4.0 libpciaccess0 libsm6 libtcl8.6 libtk8.6 libtxc-dxtn-s2tc0 libx11-6 libx11-data libx11-xcb1 libxau6 libxaw7 libxcb-dri2-0 libxcb-dri3-0 libxcb-glx0 libxcb-present0 libxcb-shape0 libxcb-sync1 libxcb1 libxcomposite1 libxdamage1 libxdmcp6 libxext6 libxfixes3 libxft2 libxi6 libxinerama1...`. Is there an option without the ridiculous 186 MB of dependencies? – Fake Name Jul 30 '17 at 05:45
  • @FakeName That's relative. For me, installing expect-devel (CentOS) has a download size of 2.2 MB and an installed size of 5 MB with only 2 deps (expect and tcl). But I already had the other deps! Anyway, you could try **tgr**'s suggestion: https://stackoverflow.com/questions/3515208/can-colorized-output-be-captured-via-shell-redirect – Samuel Aug 04 '17 at 15:33
  • 9
    You don't need the `expect-dev` package. `expect` is enough. – Yajo Sep 04 '18 at 09:53
  • The [cleanest solution](https://superuser.com/a/1434381/1183493) in this thread with a [bonus function](https://superuser.com/a/1641398/1183493). – young_souvlaki Apr 13 '21 at 01:11
  • @eamonn-obrien-strain When I insert `unbuffer` before `exec`, : `unbuffer exec > > (tee $LOG_FILE) 2>&1;`, I'm getting error: `couldn't execute "exec": no such file or directory while executing "spawn -noecho exec" ("eval" body line 1) invoked from within` `"eval [list spawn -noecho] $argv" invoked from within "if {[string compare [lindex $argv 0] "-p"] == 0}` `{ # pipeline set stty_init "-echo" eval [list spawn -noecho] [lrange $argv 1 end] | clo..." (file "/usr/bin/unbuffer" line 13)`. How do I solve this please. Thank you. – Jags Jun 09 '21 at 13:07
  • @eamonn-obrien-strain My question on AskUbuntu: `https://askubuntu.com/q/1344347/928088`. Thanks – Jags Jun 09 '21 at 14:10
18

Use the ls option --color=always

--color=auto will not color output to a pipeline - for obvious reasons.

The ls man page says the following:

With --color=auto, color codes are output only if standard output is connected to a terminal (tty).

RedGrittyBrick
  • 81,981
  • 20
  • 135
  • 205
  • 2
    OK. That explains it. But can I still somehow see the colors on the screen? (It's a TTY after all). I don't mind NOT having them in the logfile, but I surely want them on my screen. – Paweł Gościcki Nov 02 '11 at 10:46
  • I think I made myself not clear enough. `ls -l` was just an example. I have a completely different command (heroku logs) that strips colors when piped to `tee`. And I want to "fix/change" tee/pipe, not the command I'm executing. – Paweł Gościcki Nov 02 '11 at 12:00
  • 1
    @Pawel, you can't easily fix it in tee/pipe as tee/pipe are not stripping these color codes. The problem is that the initial command sees it is not writing to the terminal. You need a [pseudo-terminal](http://linux.about.com/od/ttl_howto/a/hwtttl07t02.htm) that acts like a pipe but which commands see as a terminal. – RedGrittyBrick Nov 02 '11 at 16:10
  • Hm... fair enough. I guess I just need to accept that it's how it is. – Paweł Gościcki Nov 03 '11 at 15:08
  • @PawełGościcki have a look at /usr/local/heroku/lib/heroku/helpers/log_displayer.rb:20 and remove the condition that checks if you're on a terminal! – Johann Philipp Strathausen May 30 '13 at 15:45
  • 5
    @PawełGościcki this answer only fixes the problem for `ls`. See my answer that fixes the problem for all programs, including heroku logs. – Eamonn O'Brien-Strain Jun 16 '14 at 03:25
  • 2
    grep also has this option – Jack Wasey Aug 07 '17 at 19:28
14

I'll expand the script solution given in the comment of the accepted answer. Using script may be useful in case you can't or don't want to install the expect package that contains the unbuffer command.

Print ls output to stdout and file with color codes:

script -efq output.log -c "ls -l --color=auto"

where (man script):

  -e, --return
         Return the exit code of the child process.  Uses the same
         format as bash termination on signal termination exit code is 128+n.
  -f, --flush
         Flush output after each write.  This is nice for telecooperation:
        one person does `mkfifo foo; script -f foo', and another can 
        supervise real-time what is being done using `cat foo'.
  -q, --quiet
         Be quiet (do not write start and done messages to either 
         standard output or the typescript file).

View the output file with colors:

less -r output.log
Juuso Ohtonen
  • 1,025
  • 1
  • 9
  • 15
  • 2
    `-e` is the same as `--return` - no need for both; `-efq` is `--return --flush --quiet`. – Noel Maersk May 13 '19 at 13:37
  • @NoelMaersk Thanks. I incorporated the parameter explanations in the answer. – Juuso Ohtonen May 14 '19 at 04:11
  • Perfect alternative for `unbuffer` thank you! – Mark Jun 12 '20 at 23:44
  • Clean! I turned it into a function with usage `output file command`. SO doesn't support this much code in a comment so I put it [here](https://superuser.com/a/1641398/1183493). – young_souvlaki Apr 13 '21 at 01:08
  • Look my answer for a more general approach. The script just forward the input to stdout instead of a file. Then it can be used for piping to a command directly – nadapez May 10 '22 at 00:35
3

script -efq -c "ls --color=auto" >(cat) | tee output.log

For convenience I created this script:

#!/usr/bin/bash
script -efq -c "$*" >(cat)

call it say unbuff and then

unbuff ls --color=auto | tee output.log
nadapez
  • 183
  • 5
  • Excellent! I came up with a solution but it required writing to a temp file and `cat`ing it back out (need to pipe into grep). The `>(cat)` saves that whole workaround. The version of `script` on my machine only takes `[-adkpqr]` args but this little function ended up working great: `unbuff () { script -q >(cat) $* >/dev/null ; }` – Keego Aug 03 '22 at 15:41
2

Here is a function based off this clean answer that I couldn't fit in the comments.

output()
{
    output_file=$1
    shift
    command=$@
   
    script -efq $output_file -c "$command"
    less $output_file
}

Usage

output file command