3

I am running the latest mac OS and using zshell, and having trouble writing a shell script. I do most of my work from the command line, and wanted to have a bash script automatically save and log all of my input commands and output.

My most obvious choice was the bash "script" command, generally initiated on the command line as

script -a <filename>.txt

I wrote a shell script, log.sh, whose contents are below:

#! /bin/sh
echo "Welcome to the jungle."

# get date
now=$(date +%Y_%m_%d)

# create filename
fname='log_'$now'.txt'

# file save location
floc=~/path/to/directory/$fname

# start script
script -a -q  $floc

This starts a script where the filename is today's date and saved in a designated directory. This works fine, except, however, the contents that are written to the file. Instead of just the plan text input/outputs I would expect, I get what I believe to be ANSI characters that make the file difficult to read.

Here is what my command line looks like:

~$ bash log.sh      
Welcome to the jungle.
~$ echo "Hello world"
Hello world
~$ exit

Saving session...
...copying shared history...
...saving history...truncating history files...
...completed.

And how it is recorded in the log file:

[1m[7m%[27m[1m[0m                                                                                                  
 
]7;file://<myname>/Users/<myusername>
[0m[27m[24m[J[36m<myusername>  [38;5;246m~[39m$ [K[?2004heecho "Hello world! "[?2004l

Hello world
[1m[7m%[27m[1m[0m                                                                                                  
 
]7;file://<myname>/Users/<myusername>
[0m[27m[24m[J[36m<myusername> [38;5;246m~[39m$ [K[?2004heexit[?2004l


Saving session...
...copying shared history...
...saving history...truncating history files...
...completed.

I am aware there are a few ways to handle the ANSI keys after creating the file, such as using the cat command to display the text, or a perl script to remove the keys; However, using the cat command is annoying when I am trying to look through a large file, and having to cleanup the file manually is tedious. Is there any way I can record my command line inputs and outputs without the ANSI keys?

Thank you!

Nicole I.
  • 31
  • 1
  • This answers your question: [Convert tty terminal output to plain text](https://unix.stackexchange.com/questions/274530/convert-tty-terminal-output-to-plain-text) – Thomas Dickey Apr 04 '23 at 22:43
  • In the default macOS Terminal app, `⌘` + `S` will save the session contents to a text file that does not appear to have the color escapes. There's reportedly a similar option in [iTerm2](https://stackoverflow.com/a/69134627). – Gairfowl Apr 05 '23 at 07:43

2 Answers2

0

the easy way out is to look at the file with less -R. That is not exactly what you ask for, but might be helpful anyway.

markgraf
  • 2,849
  • 1
  • 8
  • 20
0

This appears to work:

#!/usr/bin/env zsh
{
    # Create a named pipe, using a temp file name.
    mkfifo -m 600 ${fifo::=$(mktemp -u)}

    logFile="~/logs/log-$(date +%F-%H-%M-%S).txt"
    print "Logging to $logFile"

    # Launch the filter in the background (&), so it will
    # run in parallel with the script utility.
    # This will read from the named pipe, remove the ansi
    # color escapes, and write the result to the log.
    # (perl source: https://superuser.com/a/1388860).
    perl -pe 's/\e\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]//g;
              s/\e[PX^_].*?\e\\//g;
              s/\e\][^\a]*(?:\a|\e\\)//g;
              s/\e[\[\]A-Z\\^_@]//g;' \
        < $fifo > ${~logFile} &

    # Get the process id of the background process.
    fifoPid=$!

    # Launch the script utility, capturing data from the
    # interactive shell. -F ensures writes are immediately
    # available in other processes.
    script -q -F $fifo
    # script -q -F $fifo bash

    # The background process will end when the script
    # utility closes its handle to the fifo, i.e. when the
    # shell launched by 'script' exits. But it may not exit
    # immediately, so it could be running when we get to
    # this point. This will bring it to the foreground, just
    # in case some other actions follow this script.
    wait $fifoPid

} always {
    # cleanup temp fifo.
    if [[ -p $fifo ]] rm $fifo
}

This uses a named pipe so that the output from script is filtered in real time. You could also run the cleanup filter as a post process in the shell program that launches script, but that's not nearly as interesting.

Gairfowl
  • 478
  • 2
  • 6