<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Knowledge Base (Posts about tee)</title><link>https://bgstack15.ddns.net/blog/</link><description></description><atom:link href="https://bgstack15.ddns.net/blog/categories/tee.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2022 &lt;a href="mailto:bgstack15@gmail.com"&gt;bgstack15&lt;/a&gt; 
&lt;a rel="license" href="https://creativecommons.org/licenses/by-sa/4.0/"&gt;
&lt;img alt="Creative Commons License BY-SA"
style="border-width:0; margin-bottom:12px;"
src="https://bgstack15.ddns.net/.images/l_by-sa_4.0_88x31.png"&gt;&lt;/a&gt;</copyright><lastBuildDate>Sun, 27 Feb 2022 04:05:22 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Linux use tee with color</title><link>https://bgstack15.ddns.net/blog/posts/2017/03/21/linux-use-tee-with-color/</link><dc:creator>bgstack15</dc:creator><description>&lt;h2&gt;Output in color on the console&lt;/h2&gt;
&lt;p&gt;The console normally can display color. The most obvious example is when you
do a standard ls command. By default on most systems, ls includes a
--color=auto flag that changes filenames of certain types to different colors.
Some applications display colorized output like systemctl status nfsd.service.&lt;/p&gt;
&lt;h2&gt;Using tee&lt;/h2&gt;
&lt;p&gt;Tee is a nifty cli utility that duplicates the input to both the standard out
as well as to a file. You can use it like so:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ls -l --color=always | tee ~/ls.out
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Some tools will disable colorized output if it is going to a pipe, but ls can
be forced to provide color with the --color=always flag. For applications that
don't have such an option, you can usually prepend the command &lt;em&gt;unbuffer&lt;/em&gt;.&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;unbuffer ansible-playbook playbook.yml | tee ansible.log
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;When you examine the file, however, you will observe that the console color
commands were saved. That's perfectly fine if you cat the file, or less -R,
but normally you want a text file to just have the text. Sysadmins are quite
used to dealing with logs that are all the standard color of the console.&lt;/p&gt;
&lt;h2&gt;The solution: ctee!&lt;/h2&gt;
&lt;p&gt;Using some fantastic resources on the Internet which I list at the bottom of
this post, I assembled a tool that will tee the input, and send color to the
stdout (usually the console) and send regular text to the file. I call my
creation
&lt;a href="https://gitlab.com/bgstack15/bgscripts/-/blob/master/src/usr/bin/ctee"&gt;ctee&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Code walkthrough&lt;/h3&gt;
&lt;p&gt;So this script was generated using newscript which copies the template from
the same &lt;a href="https://gitlab.com/bgstack15/bgscripts"&gt;bgscripts&lt;/a&gt; package. The
template sources framework, which is my big flagship library of functions for
shell scripts. Lines 39-54 are the parseFlag function, which parses the
command line passed parameters. I implemented this before I ever learned about
Python's argparse library. The only flag worthy to note is --append. All it
does is set the variable to 1. The meat of this script starts at line 85 and
goes to the end, which is shown below.&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;# this whole operation calculates where the stdout of this whole pipe goes
ttyresolved=0
_count=0
_pid=$$
_fd1="$( readlink -f /proc/&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;_pid&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;/fd/1 )"
while test &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;ttyresolved&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; -lt 10;
do
   ttyresolved=$(( ttyresolved + 1 ))
   echo "before &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;ttyresolved&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;, _pid=&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;_pid&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;, _fd1=&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;_fd1&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;" &amp;gt; &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;devtty&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;
   case "&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;_fd1&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;" in
      *pipe:* )
         newpid=$( find /proc -type l -name '0' 2&amp;gt;/dev/null | xargs ls -l 2&amp;gt;/dev/null | grep -F "$( basename &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;_fd1&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; )" | grep -viE "\/&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;_pid&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;\/"; )
         newpid=$( echo "&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;newpid&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;" | head -n1 | grep -oiE "\/proc\/[0-9]*\/" | grep -o "[0-9]*" )
         _pid=&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;newpid&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;
         _fd1="$( readlink -f /proc/&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;_pid&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;/fd/1 )"
         ;;
       *dev*|*/* )
         thisttyout="&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;_fd1&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;"
         ttyresolved=10
         ;;
   esac
done

echo "thisttyout &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;thisttyout&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;" &amp;gt; &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;devtty&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;

# MAIN LOOP
case "&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;" in
   1)
      tee &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;thisttyout&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; | sed -u -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g" &amp;gt;&amp;gt; "&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;outfile1&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;"
      ;;
   *) tee &lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;thisttyout&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt; | sed -u -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g" &amp;gt; "&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;outfile1&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;"
      ;;
esac
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The idea behind ctee is that it uses tee, but inverts the outputs. Ctee uses
the console tty as the file to output to, and then standard out continues on
to a sed command that strips out the console color escape sequences which is
then redirected to the file specified to ctee as a parameter. So one of the
big tricks is to derive which console you are executing this command from. The
while loop from lines 90-106 use a /proc filesystem trick to find the process
on the other end of a pipe. This loop travels from this process's standard out
to the standard in of another process, and then takes that standard out, and
tracks that down until it includes the phrase "dev" or "/" in its name,
indicating either a tty or a file (technically the dev is redundant, but it
makes the line easier to understand). The second main trick of ctee is that it
has a sed command strip out the console color control characters. I found a
nice way to remove that, and you can see those commands on lines 113 and 115.
One line is for appending to a file, and the other is for overwriting.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;You can use ctee.sh like so:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;unbuffer&lt;/span&gt; &lt;span class="n"&gt;ansible&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;playbook&lt;/span&gt; &lt;span class="n"&gt;playbook&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;yml&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ctee&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ansible&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;playbook&lt;/span&gt;&lt;span class="o"&gt;.$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="s2"&gt;"+%Y-%m-&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s2"&gt;-%H%M%S"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Which will get your console the colorized output that helps for easy
interpretation of the output, with the normal logging for later analysis.&lt;/p&gt;</description><category>color</category><category>linux</category><category>shell</category><category>tee</category><guid>https://bgstack15.ddns.net/blog/posts/2017/03/21/linux-use-tee-with-color/</guid><pubDate>Tue, 21 Mar 2017 13:26:37 GMT</pubDate></item></channel></rss>