2

I captured the output of a script that uses tput to draw certain things on screen. When I perform cat myoutput then everything is well seen (looks like terminal reinterprets it from beginning), but when I edit or pipe that output I see plenty of ansi sequences and all the stuff previous to destructive printing like tput clear and the like.

How can I a postprocess it so I only get the final "render"?

Even better, the origin of this is that I am currently teeing my script so it prints everything to a file aside from to the terminal

with exec > >(tee /dev/tty)

is there a way to tell the stdout channel to "render" everything before saving?

1 Answers1

2

What you want is a program that understands these terminal control sequences, and is able to render the final view. Well, such a program is called a terminal emulator. Some of them are graphical – like the program you launch to use your shell, e.g., gnome-terminal or alacritty, others are primarily headless.

The older screen or the more modern tmux are the relevant ones here.

  1. write an "outer" script:
    1. create a named pipe
    2. Start your "inner" script (so the one that outputs stuff) in tmux, in the background
    3. in your outer script, read from the fifo (this blocks because nothing has been written),
    4. once that read finishes, instruct tmux to output a screenshot
  2. in your inner script, write something to the named pipe to signal you're at a state to be taken a screenshot of

Putting it together, something like

#!/usr/bin/zsh
# outer script
# SPDX-License-Identifier: WTFPL

Make a temporary directory

tmpdir="$(mktemp -d)"

Prepare the FIFO

fifo="${tmpdir}/fifo" mkfifo "${fifo}"

Start the inner script in tmux

tmux new-session -d -s "${tmpdir}" -e "FIFO=${fifo}" ./inner-script.sh … #^ ^ ^ ^------+-----^ ^------+--------^ ^ ^ #| | | | | | | #------run tmux, the terminal emulator

| | | | | |

---in tmux, run the "new-session" command to, well, get a new session

| | | | |

---detach that session, i.e. don't connect it to the current terminal

| | | |

--specify the session name. Conveniently, we abuse the name

of our temporary directory, as it's inherently unique

| | |

-- for the started command, set the environment

variable FIFO to the name of our FIFO

| |

-- launch your script

…with its arguments--/

Wait for something to be written to our FIFO

cat "${fifo}" > /dev/null

instruct tmux to take a "pane shot"

tmux capture-pane -t "${tmpdir}" -p > "/path/to/capture"

^------+-----^ ^ ^---------+--------^

| | |

-------------------------------- target (session name as above)

| |

----------------------- print to stdout

|

----------- redirect stdout to file

Finally, clean up session and temporary directory

tmux kill-session -t "${tmpdir}" rm -rf "${tmpdir}"

You only need to add the writing of something to the fifo to your inner-script.sh, e.g., echo done > "${FIFO}"; sleep 100.

If you already have a "recorded" output, your inner-script.sh might simply be cat recording.txt; echo done > "${FIFO}"; sleep 100

  • Absolutely amazing answer. I did a simplified test and works perectly. Ill wait before approving just in case somebody comes up with a simpler creative solutio/shortcut, but I plan to eventually check your answer. Thanks again!!!! – Whimusical Mar 19 '24 at 23:22
  • Played a bit with you answer, ended up adjusting it to my case, it works. And now looking for ways to optimize it https://unix.stackexchange.com/questions/772947/redirecting-stdout-of-a-script-as-the-stdin-of-another-script-command-running-in – Whimusical Mar 23 '24 at 00:21