Sopalajo de Arrierez
Sopalajo de Arrierez

Reputation: 3860

Sending ANSI-Colored codes text to 3 outputs: screen, file and file filtering ANSI codes

My program is sending characters-colored text to some log file:

echo -e "\\033[38;5;2m-->\\033[0m Starting program." | tee $LogFile -a

Resulting in a perfectly colored log line, but I would like to simultaneously create another logfile without ANSI codes, due to I need to browse this log in Windows (I know there are some ANSI browsers for Windows, but I prefer to browse the log files using Total Commander, that has no decent plugin for that).

So, I need three outputs in my log line:

The above line was fine thanks to the tee command, that solves points 1 and 2, but I don't know how to add some option to save to another file but without ANSI codes, at least without replicating the full line. Maybe using redirectors, file descriptor, with the help of mkfifo?

My workaround for now is, by the way, duplication of outputs (what becomes a bit awkward):

echo -e "\\033[38;5;2m--> Starting program.\\033[0m" | tee $LogFile -a
echo "--> Starting program." >> $NotANSILogFile

Upvotes: 2

Views: 1162

Answers (2)

John1024
John1024

Reputation: 113864

To send the ansi codes to a log file, ansi.log, and to the screen, while also sending a non-ansi version to a log file called nonansi.log, use:

echo -e "\\033[38;5;2m-->\\033[0m Starting program." | tee -a ansi.log | tee /dev/tty | sed $'s/\E[^m]*m//g' >>nonansi.log

How it works

  • tee -a ansi.log

    The first tee command appends the ansi-encoded string the the log file ansi.log.

  • tee /dev/tty

    The second tee command sends the ansi-encoded string to the screen. (/dev/tty is the file name of the current screen.)

  • sed $'s/\E[^m]*m//g' >>nonansi.log

    The final command, sed removes the ansi sequences and the result is appended to nonansi.log.

    The sed command is contained in a bash $'...' string so that the escape character can be represented simply as \E. This substitute command looks for sequences that start with escape, \E, and end with m and removes them. The final g tells sed to perform this substitution on every escape sequence on the line, not just the first one.

    If you have GNU sed, an alternative is to use GNU's \o notation instead:

    sed 's/\o033[^m]*m//g' >>nonansi.log
    

    If you have neither GNU sed nor bash, then you need to see what facilities your sed or your shell provide for the Esc character.

Hiding the details in a shell function

If you need to make multiple log entries, it might be easiest to create a shell function, logger, to hold all the messy details:

logger() { echo -e "$*" | tee -a ansi.log | tee /dev/tty | sed $'s/\E[^m]*m//g' >>nonansi.log; }

With this function defined, then any log entry can be performed by providing the ansi-encoded string as an argument:

logger "\\033[38;5;2m-->\\033[0m Start."

Upvotes: 3

Sopalajo de Arrierez
Sopalajo de Arrierez

Reputation: 3860

This method works (needs perl installed) to send output to screen (ANSI) and two files: $LogFile (ANSI) and $LogFile.plain (ANSI-free):

echo -e "\\033[38;5;2m--> Starting program.\\033[0m" | tee >(perl -pe 's/\e\[?.*?[\@-~]//g'>>"$LogFile".plain) $LogFile -a 

Details:

  • The tee command splits output to both screen(stdout) and to the perl command.
  • The perl line filters the ANSI codes.
  • The output of perl is redirected to the plain Non-ANSI log file (used LogFile.plain for its filename).

To send output only to files (not to screen) do (same with just a classic >/dev/null).

echo -e "\\033[38;5;2m--> Starting program.\\033[0m" | tee >(perl -pe 's/\e\[?.*?[\@-~]//g'>>"$LogFile".plain) $LogFile -a >/dev/null

Notes:

  • Used >> instead of >, as expected for an incremental log file.
  • Used -a for the tee command for the same reason above.
  • I would prefer to use sed instead of perl, but all the sed examples I have found until now do not work. If someone knows, please report.

Upvotes: 2

Related Questions