How to "Reverse" Cat -a?

Consider the following:

$ cat -A input.txt
Hello^IWorld$
newline$

Here, cat -A takes actual newlines and tabs, i.e. the real characters, and converts them to representations.

Is there a shell way or command-line application in Ubuntu repositories that would allow taking representations of unprintable characters and output the real values ?

In a sense, I'm asking whether there's something analogous to $'Hello\tWorld\nnewline\n', except instead of C-quoted strings, I want use "shell-quoted" strings.

5

1 Answer

Well, Python to the rescue!

Check out this one-liner, which reads from STDIN and prints to STDOUT, handling all possible "caret escapes"/"C0 codes" (like ^I) and line-end indicators ($):

python3 -c 'import sys,re;print(re.sub(r"\^([A-Z?@[\\\]^_])",lambda m:chr((ord(m.group(1))-64)&127),sys.stdin.read().replace("$\n","\n")))'

Actually it's both compatible with python (2) and python3. Here's a longer, more readable version doing basically the same:

#!/usr/bin/env python3
import sys, re

# read everything from stdin and remove line-end indicators
s = sys.stdin.read().replace("$\n", "\n"))

# replace caret escapes like ^I or ^M and output to stdout
print(re.sub(r"\^([A-Z?@[\\\]^_])", lambda m: chr((ord(m.group(1)) - 64) & 127), s)

So, first we remove the line-end indicators $.

Second we use the regular expression pattern \^([A-Z?@[\\\]^_]) to find all valid characters following carets and replace both with the correct unescaped character, according to Wikipedia on Caret notation and C0 control codes. Note how only capital letters A-Z or one of ?@[\]^_ have a special meaning.

Now to unescape such a C0 code, we take the position in the alphabet of the character succeeding the caret (found in m.group(1)), e.g. "A" is 1, "B" is 2 and so on. This is equal to its ASCII value minus the ASCII code of "A" plus one, which makes up the -64, which also explains e.g. "@" (ASCII 64) being 0 or "[" (ASCII 91) being ESC (ASCII 27). We do a binary AND operation on this number with 127 to only consider the first 7 bits of information, so that e.g. "?" (ASCII 63 == 64-1) wraps around to 127, representing the DEL character.

Finally after all these highly complex computations are done, we simply print the resulting string to STDOUT again.

2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Sarah Jenkins

Sarah Jenkins

Senior Technology Editor & AI Specialist

Sarah Jenkins is a veteran tech journalist with over 12 years of experience covering artificial intelligence, mobile innovations, and digital ethics. Her insights have appeared in leading technology publications worldwide.

Share this article
Twitter Facebook Pinterest