42

Whenever I use grep, and I pipe it to an other program, the --color option is not respected. I know I could use --color=always, but It also comes up with some other commands that I would like to get the exact output of that command as the output I would get if I was in a tty.

So my question is, is it possible to trick a command into thinking that the command is run inside a tty ?

For example, running

grep --color word file # Outputs some colors
grep --color word file | cat # Doesn't output any colors

I'd like to be able to write something like :

IS_TTY=TRUE grep --color word file | cat  # Outputs some colors

This question seems to have a tool that might do what I want :empty - run processes and applications under pseudo-terminal (PTY), but from what I could read in the docs, I'm not sure it can help for my problem

Community
  • 1
  • 1
edi9999
  • 19,701
  • 13
  • 88
  • 127
  • 1
    you realize that `--color` changes the actual data stream that is sent to command further down the pipeline? The consequence would be breaking other-wise reasonable code because `searchTarget` is not `^[32;4gsearchTarget^[32;h` (or similar). Good luck. – shellter Oct 02 '15 at 16:17
  • 1
    Yes I realize that, I will in my scripts never use that technique to color greps, but some commands that I use seem to output something different when used outside a tty (for example `ag` , the silversearcher, but also mocha, ...) and I don't have time to learn all the options to print with the same format whenever I pipe those commands into others) – edi9999 Oct 02 '15 at 16:34
  • Hmmm.. I think this has come up before with some surprising answers (to me). Did you search much for `[linux] (or) [bash] --color`? Good luck. – shellter Oct 02 '15 at 16:51
  • I searched a bit, but haven't found anything that would solve my issue. – edi9999 Oct 03 '15 at 09:33
  • 3
    Have you tried any of these: http://stackoverflow.com/questions/1401002/trick-an-application-into-thinking-its-stdin-is-interactive-not-a-pipe http://stackoverflow.com/questions/4233808/piping-data-to-linux-program-which-expects-a-tty-terminal http://rachid.koucha.free.fr/tech_corner/pty_pdip.html – Carlos Porta Oct 03 '15 at 13:11

1 Answers1

46

There are a number of options, as outlined by several other Stack Overflow answers (see Caarlos's comment). I'll summarize them here though:

  1. Use script + printf, requires no extra dependencies:

     0<&- script -qefc "ls --color=auto" /dev/null | cat
    

    Or make a bash function faketty to encapsulate it:

     faketty () {
         script -qefc "$(printf "%q " "$@")" /dev/null
     }
     faketty ls --color=auto | cat  
    

    Or in the fish shell:

     function faketty
         script -qefc "(printf "%q " "$argv")" /dev/null
     end
     faketty ls --color=auto | cat 
    

    (credit goes to this answer)

    http://linux.die.net/man/1/script

  2. Use the unbuffer command (as part of the expect suite of commands), unfortunately this requires an extra package install, but it's the easiest solution:

     sudo apt-get install expect-dev   # or brew install expect
     unbuffer -p ls --color=auto | cat  
    

    Or if you use the fish shell:

     function faketty
         unbuffer -p $argv
     end
     faketty ls --color=auto | cat 
    

    http://linux.die.net/man/1/unbuffer

This is a great article on how TTYs work and what Pseudo-TTYs (PTYs) are, it's worth taking a look at if you want to understand how the linux shell works with file descriptors to pass around input, output, and signals. http://www.linusakesson.net/programming/tty/index.php

Nick Sweeting
  • 5,364
  • 5
  • 27
  • 37
  • 2
    Add `-e` to `script` to return the return value of the command. Also, consider piping to `less -FXur` which will display colours and not do anything if the output fits on a single screen (and fix a \r issue if you have it). – Tom Hale Sep 06 '16 at 04:58
  • `script: illegal option -- f` on macosx – Mike Graf Aug 01 '18 at 21:05
  • `script -t 0` is equivalent to `script -f`, you can use those options instead on Mac. – Nick Sweeting Aug 04 '18 at 10:15
  • 4
    Swop the c and the e `script -qfec "$(printf "%q " "$@")"` – Erik Martino Jan 30 '19 at 10:03
  • I was getting error with the "e" option last as @ErikMartino pointed out. Swapping made it work. – StevieD Mar 27 '19 at 01:25
  • 1
    @StevieD I just hit the same issue and edited to make this change. – Elliot Cameron Apr 03 '19 at 21:39
  • How can I use emualte tty when I need to run the program with `exec`, i.e. to keep the original shell PID? – mvorisek Jun 20 '19 at 01:41
  • @Mvorisek If you mean you want the child process's PID to take over and not `unbuffer`/`script`'s PID, then I don't think that's possible because both `script` and `unbuffer` are wrapper commands around the actual command, so by necessity they need different PID as long as the child command is running. – Nick Sweeting Jun 20 '19 at 19:57
  • 2
    See https://stackoverflow.com/a/60279429/1356047 for a version that works on both Linux & MacOs AND supports returning status code – Jonas Berlin Feb 18 '20 at 11:06
  • 1
    It must have been a while since this answer was written, but I just checked the install size of `expect` on archlinux and it is less than half 1MB – smac89 Jun 19 '21 at 20:10
  • This only works on GNU systems, not BSD, so the "no dependencies" bit is far from correct. You need to add a switch to run different commands depending on the os. – oligofren Nov 05 '21 at 20:32
  • @oligofren <1% of public systems run BSD, while I personally love BSD and run FreeBSD myself on one of my machines, you cannot reasonably expect every SO answer to be tailored towards BSD audiences. One of the skills BSD users rapidly acquire is being able to translate Linux solutions to BSD equivalents. If you have a correction you'd like to share for BSD, post it in comments! – Nick Sweeting Nov 12 '21 at 15:44
  • macOS is BSD. That's why it's an important user group – oligofren Nov 13 '21 at 20:48
  • 1
    The `unbuffer` solution should work on macOS and other BSDs as far as I can tell. `brew install expect` or `pkg install expect`. – Nick Sweeting Nov 17 '21 at 01:43
  • 1
    Concerning option 1, I'd recommend keeping the `/dev/null` part of the [original answer](https://stackoverflow.com/a/20401674/4070848) or else you end up generating a `typescript` file with some content you probably don't need or even want. – drmrbrewer Dec 09 '21 at 09:39
  • 1
    Added it back, thanks @drmrbrewer – Nick Sweeting Mar 22 '22 at 23:05
  • I get `/bin/bash: script: command not found` – theonlygusti Apr 18 '23 at 16:16
  • Most distros include `script` out-of-the-box, but if you're on a system that doesn't have it you'll have to install the `util-linux` package @theonlygusti. e.g. `apt install util-linux` or `brew install util-linux`. – Nick Sweeting Apr 20 '23 at 02:15