title: POSIX shell function: printing arguments as a TSV record
body: <p>I'm writing a POSIX shell function that prints its arguments as a TSV record.
  <br>
  Each argument is escaped with the following rules:</p>
  <ul>
  <li><code>\n</code> for newline</li>
  <li><code>\t</code> for tab</li>
  <li><code>\r</code> for carriage return</li>
  <li><code>\\</code> for backslash</li>
  </ul>
  <p>Here is the function:</p>
  <pre class="lang-sh prettyprint-override"><code>#!/bin/sh
  
  tsv_print() {
      rec=
      for str in &quot;$@&quot;
      do
          esc=
          i=${#str}
          until [ $i -eq 0 ]
          do
              end=&quot;${str#?}&quot;
              chr=&quot;${str%&quot;$end&quot;}&quot;
  
              case $chr in
              &quot;$__TAB__&quot;) chr='\t' ;;
              &quot;$__LF__&quot;) chr='\n' ;;
              &quot;$__CR__&quot;) chr='\r' ;;
              \\) chr='\\' ;;
              esac
  
              esc=&quot;$esc$chr&quot;
              str=&quot;${end}&quot;
              i=$((i-1))
          done
          rec=&quot;$rec${rec:+&quot;$__TAB__&quot;}$esc&quot;
      done
  #   echo &quot;$rec&quot;
      printf '%s\n' &quot;$rec&quot;
  }
  </code></pre>
  <p>With the — <em>painful to read in code</em> — characters stored beforehand as follows:</p>
  <pre><code>__TAB__=$(printf '\t')
  __CR__=$(printf '\r')
  __LF__=&quot;
  &quot;
  </code></pre>
  <hr />
  <p>I would like to know:</p>
  <ol>
  <li><p><s>Why doesn't my code escape the characters at all?</s></p>
  <p><sup><em><strong>edit:</strong></em> As @GordonDavisson pointed out, <code>echo</code> was the
  culprit!! Using <code>printf</code> seems to be the only portable way, with the cost of a possible fork.</sup></p>
  </li>
  <li><p>Is there a better, POSIX compliant, method to do it? <code>awk</code> and <code>sed</code> don't seem appropriate for the job...</p>
  </li>
  <li><p>How would you do the un-escaping?</p>
  <p><sup><em><strong>edit:</strong></em> As @KamilCuk posted in his <a href="https://stackoverflow.com/a/70049950/3387716">answer</a>, a <code>printf '%b'</code> would suffice; the TSV record has the correct format for that.</sup></p>
  </li>
  </ol>
  <hr />
  <h5>postscript</h5>
  <p>In the end, the function wasn't needed because the input didn't contain any character to escape. That said, the input format wasn't that straight-forward to convert. It was a <a href="https://en.wikipedia.org/wiki/Self-defining_Text_Archive_and_Retrieval" rel="nofollow noreferrer">STAR File</a> with varying number of columns per line (limiting the lines to 80 characters max) and containing quoted strings...</p>
  <p>input:</p>
  <pre><code>...
  loop_ 
   _refl_0201 _refl_0012 _refl_2003 _refl_1600 _refl_1304 _refl_1305 _refl_1800
   _refl_1801 _refl_1802 _refl_1803 _refl_1804 _refl_1805 _refl_1806 _refl_1701
   _refl_1700 _refl_1202
  '0 0 6' .147364 Z000020c1 .41 1 78.45 3.501 35.2221 -35.2221 0 -1.6055 -3.0963
  -36.7288 -5.0964 39.3109 5.909983 '0 0 12' .294551 Z000010c1 .9 1 48.44 2.3805
  39.910008 39.9101 .268379-04 1.75598 3.09745 41.6656 3.09809 47.8384 0 .939517
  ...
  </code></pre>
  <p>output (separators are tabs):</p>
  <pre><code>_refl_0201 _refl_0012 _refl_2003 _refl_1600 _refl_1304 _refl_1305 _refl_1800 _refl_1801 _refl_1802 _refl_1803 _refl_1804 _refl_1805 _refl_1806 _refl_1701 _refl_1700 _refl_1202
  '0 0 8' .147364 Z000020c1 .41 1 78.45 3.501 35.2221 -35.2221 0 -1.6055 -3.0963 -36.7288 -5.0964 39.3109 5.909983
  '0 0 14' .294551 Z000010c1 .9 1 48.44 2.3805 39.910008 39.9101 .268379-04 1.75598 3.09745 41.6656 3.09809 47.8384 0.939517
  ...
  </code></pre>