1

EDIT:

  • specifically what worked for me was using the python script in this answer
  • and I gave the tweaks I used to get it working for my autoloading functions in fishshell, and on ubuntu 20.04, in this comment on that answer


There are lot of commandline programs that apparently check whether their output is stdout a terminal or a pipe/file, and tweak what they output based on that. Those tweaks basically mainly being stripping out ansi-color-codes if the output is not stdout a terminal.

At least some of these programs have their own idiosyncratic configuration options to get them to still output ansi-color-codes, but is there any sort of completely program-agnostic way of doing it?

Like, something I could somehow quickly and simply "wrap around" arbitrarily any program I want the output from, in order to "fool" it into "thinking" its output was still just stdout a terminal?


Use-case example:

Say you want to take the output of eix on Gentoo or apt search on Ubuntu (both just search available packages by some regex), and pipe the output into less, but keeping the default color highlighting.

Or like, I have a shell function that takes ansi-color-code output, converts it to html (using the program "aha"), and copies it into my clipboard.

eix happens to have a flag --force-color, but apt search apparently doesn't have anything like that. (And grep uses --color=always, demonstrating the whole "even if a program has the option, how to use it will be idiosyncratic" thing.)

Owen_AR
  • 186
  • 2
    "The output is not stdout" is incorrect. The output is always stdout, because stdout is just the file descriptor the program writes to by default. That never changes. But you can assign stdout to file, device, pipe etc. What you actually asking about is probably how to distinguish if the output (stdout) is assigned to a terminal or something that is not a terminal (file or pipe). Programs use various techniques to determine if the output is a terminal or not, so I think there is no "catch-all" answer. – raj Apr 01 '21 at 19:17
  • 1
    I recommend this answer from the duplicate question. I just tested it and it seems to work as expected! – terdon Apr 01 '21 at 20:14
  • @terdon Oh thank you, that seems to just work! (I use fishshell, so I tweaked it from os.execvp(argv[1], argv[1:]) to os.execvp('fish', ('fish',) + tuple([ '-c', ' '.join(argv[1:]) ])) so I can use my autoloading functions, and... that seems to work?) – Owen_AR Apr 03 '21 at 12:36
  • @raj ... Thanks! But in that case, I'm still really confused about how the terminology works... I guess I thought... output gets printed to a terminal by default(ie "standard"), so at the point when output gets redirected from where it would "normally" get printed to a terminal, it ceases to be "standard-output"? But if that's still "standard-out", then... what if anything would be NON-standard-out? Or is it more like "no, there's just the three stdin/stderr/stdout, and the "std" is just a (synchronically arbitrary/meaningless) part of those names for historical reasons"? – Owen_AR Apr 20 '21 at 12:20
  • 1
    You need to take into account that the relation between eg. "print" (or equivalent) statement in program and the actual files/devices where the output will go is two-step. In first step, program uses a particular file descriptor to write to. In the second step, the file descriptor is associated with actual file or device. The three "standard" file descriptors, stdin/stdout/stderr, are special, because they are already open when the program starts, so the program can just use them. (cont. in next comment) – raj Apr 20 '21 at 12:52
  • 1
    Also, when you don't specify any file descriptor in the "print" command, the program uses the "standard" one. I guess that's why they are called standard. But you can (in the second step) assign these file descriptors to any files or devices outside of program by using redirection without need to change the program. The program always writes to file descriptor, not a terminal or file etc. That's not the case with "non-standard" file descriptors, where the program needs to open a specific file or device itself by "open" statement (or equivalent) before it can use the file descriptor. – raj Apr 20 '21 at 12:55
  • 1
    @Owen_AR I have made a drawing for you to better illustrate the concept of "standard" I/O: http://rafa.eu.org/share/public/stdout.png . Program is inside the rectangle, OS is outside. Think of "standard" as referring to the part of the picture indicated by red arrows and statements next to these arrows. These never change, regardless of what you assign stdin/stdout/stderr outside of the program. File descriptor 3 is a non-standard file descriptor, that program must open itself and specify the file name it will be assigned to. – raj Apr 20 '21 at 13:56
  • @raj Thanks! That pretty much makes sense now. (I did try skimming the obvious wiki pages etc before I asked you, but all the sources I found left some necessary pre-assumptions unclear.) I mean, I know I'm still shakey on this, but now thanks to you, I think should finally be able to straight-forwardly figure out all the details as I deal with practical problem involving them in the future... thanks! – Owen_AR Apr 22 '21 at 10:13
  • @rajj btw, if I am understanding correctly, then I would assume if you do something in a language like ruby like File.save( "~/some_file" ){ |that_file| that_file.puts "text" }, then the that_file name you chose to refer to the file within that block is probably underlying referring to file descriptor 3? (So presumably situations are very rare where a human would be working with references to any file descriptor of number 3 or above as that number itself, rather than by something like eg that_file or whatever?) – Owen_AR Apr 22 '21 at 10:14
  • @raj ... And my confusion therefore ultimately comes from learning that as equivalent to, in a shell language like fish, doing set that_file ~/some_file ; echo "text" > $that_file, eh? They're practically equivalent, but the ruby eg is telling it to write to file descriptor 3, whereas the fish eg is telling it to have file descriptor 2 refer to that file while executing the command echo "text"? (... if so, man that is... an extremely esoteric-feeling distinction.) – Owen_AR Apr 22 '21 at 10:32
  • @raj (blast, I accidentally wrote "file descriptor 2" (stderr) when I meant 1 (stdout), but can't edit it) – Owen_AR Apr 22 '21 at 10:59
  • 1
    @Owen_AR I don't know Ruby, but it looks like your example implicitly does the open() call, and yes, that_file is probably the file descriptor. It doesn't necessarily to be 3, the language's runtime may assign different number higher than 2, therefore we never work with explicit numbers (as they are useless before the file descriptor is actually opened), but with values obtained from open() call. As for your second question, > in shell, should redirect stdout, ie. the file descriptor 1, and assign it to $that_file. I tried to show it explicitly on my drawing :) – raj Apr 22 '21 at 11:00
  • 1
    @Owen_AR These examples are not equivalent, because redirection of FDs 0, 1, 2 occurs outside of the program, at the OS level. The echo command in your fish example (better change it to actual program like ls) still writes to FD 1 and it doesn't know where's actually writing to. In case of your Ruby example, the program explicitly opens a named file, assigns to it a new file descriptor (besides the standard three 0, 1, 2 which are not touched; they're still opened and can be used), and writes to it (blue line on my pic). – raj Apr 22 '21 at 11:06
  • 1
    @Owen_AR And in the shell example, the variable $that_file holds the file name, not the file descriptor. An equivalent example in Ruby would be to create a program that contains only puts "text" and then run it using the command: program > $that_file. – raj Apr 22 '21 at 11:08
  • @raj I just tried to contrive some situations where it could make a practical difference, and what I've thought of so far is in this pastebin. eg1.1 vs eg1.2 works like I expect, but I don't understand the result of eg2.2 -- I would've thought doing an explicit redirection of stdout to /dev/stdout (itself?) would end up doing exactly the same thing as eg2.1?? (ie, rather than directly doing fd1->file, I expected it would do fd1->fd1->file (ie, ending up in the same place). But instead it results in going to the terminal). – Owen_AR Apr 22 '21 at 11:46
  • @raj (And yeah, I had forgotten echo was a fish builtin covering up the actual program at /bin/echo... but that doesn't make a difference here, right? (eg3 shows it doesn't, right?)) – Owen_AR Apr 22 '21 at 11:46
  • @raj Also, eg4 matches (in your drawing) the arrow going from "Read from file" to "0", right? And something in a lang like ruby like File.read( path_to_some_file ) would be a blue arrow coming from "specific file named by the program, no redirection", eh? – Owen_AR Apr 22 '21 at 11:46
  • eg2.2 is a bit tricky. Redirection to /dev/stdout is performed before everything is redirected to tempfile.txt so at this time /dev/stdout is still connected to the terminal. So when redirection to tempfile.txt occurs, a new duplicate file descriptor is indirectly created that remains connected to a terminal. This is tricky programming. I would rather redirect to /dev/tty if I wanted to write explicitly to terminal. As for eg4 and the Ruby read example, you are correct :) – raj Apr 22 '21 at 15:09

0 Answers0