18

I want to use tr to do some rot13 transformation. I can beautifully understand this command:

tr A-Za-z N-ZA-Mn-za-m <<< "URYC ZR CYRNFR"

which output is HELP ME PLEASE, but I can't figure out how this other command can produce the same rot13 transformation:

tr .............A-Z A-ZA-Z <<< "URYC ZR CYRNFR"

So I have two questions:

  1. What's the magic behind the second tr command?
  2. How to make the second command work for both lower and upper case, just like the first command?
  • I know there's 13 dots. What I wanna know is how it works. There's no explanation about dots in the manual – Frederico Oliveira Apr 6 at 3:19
  • 4
    you had better hope you don't run into a dot in your input text – iruvar Apr 6 at 3:43
  • 1
    FWIW the shorter form tr [.*13].A-Z A-ZA-Z works just as well as tr .............A-Z A-ZA-Z – iruvar Apr 7 at 14:12
17

It works as follows:

SET1-> .............ABCDEFGHIJKLMNOPQRSTUVWXYZ
SET2-> ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLM

So tr will translate SET1 to SET2.

This is equivalent to first one because it is also shifting by 13 units as there 13 dots.

To include the lower case letters, you'll have to arrange them in SET1 with a similar offset, i.e.:

.............ABCDEFGHIJKLMNOPQRSTUVWXYZ..........................abcdefghijklmnopqrstuvwxyz

ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzabcdefghijklm

That's 26 dots between Z and a, spanning half the upper-case and half the lower-case alphabet. So the tr command itself will be:

tr .............A-Z..........................a-z A-ZA-Za-za-z
14

As @Prvt_Yadv says in their answer, it works because there are 13 dots.

The sets are

First set:  .............ABCDEFGHIJKLMNOPQRSTUVWXYZ
Second set: ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ

The dot isn't a special character, so if you have a dot in your input, it will be translated too. In the version of tr that I have, it is the last corresponding character in the second set, in this case an M:

$ echo URYC ZR CYRNFR. | tr .............A-Z A-ZA-Z
HELP ME PLEASEM

(I could imagine that a different version of tr might use the first matching character in set 2, which would give an A.)

To answer your second question, you need another 13 dots in the first set to "use up" the remaining uppercase letters in set 2:

First set:  .............ABCDEFGHIJKLMNOPQRSTUVWXYZ.............
Second set: ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ

then you can repeat the pattern:

First set:  .............ABCDEFGHIJKLMNOPQRSTUVWXYZ..........................abcdefghijklmnopqrstuvwxyz
Second set: ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz

which gives us:

tr .............A-Z..........................a-z A-ZA-Za-za-z

And so:

$ echo Uryc zr cyrnfr | tr .............A-Z..........................a-z A-ZA-Za-za-z
Help me please

Personally, I think the first way of doing it in your question is simpler!

The first way also doesn't transform any other characters in the input. For example, compare:

$ echo Uryc zr cyrnfr. | tr .............A-Z..........................a-z A-ZA-Za-za-z  
Help me pleasem

with

$ echo Uryc zr cyrnfr. | tr A-Za-z N-ZA-Mn-za-m
Help me please.
1

Ok, so thanks to @Prvt_Yadv I was able to understand the dots. Here's the first question answer:

What's the magic behind the second tr command?

The 13 dots are simply being mapped to the first 13 letters from the second set. So

tr .............A-Z A-ZA-Z will produce the following sets:

SET1 -> .............ABCDEFGHIJKLMNOPQRSTUVXWYZ
SET2 -> ABCDEFGHIJKLMNOPQRSTUVXWYZABCDEFGHIJKLMNOPQRSTUVXWYZ

If your input doesn't contain a dot, you can discard the initial sequence, since you won't use those substitution. Then the sets would become:

SET1 -> ABCDEFGHIJKLMNOPQRSTUVXWYZ
SET2 -> NOPQRSTUVXWYZABCDEFGHIJKLMNOPQRSTUVXWYZ

But since the first set already contains all 26 letters and set2 has repeating trailing letter, those are discarded too, finally becoming

SET1 -> ABCDEFGHIJKLMNOPQRSTUVXWYZ
SET2 -> NOPQRSTUVXWYZABCDEFGHIJKLM

Which is the rot13 substitution and identical to the first command (except for not dealing with lower cases here). The same logic can be applied for the title of the question:

tr ...A-Z A-ZA-Z <<< “JVPQBOV” would produce the sets:

SET1 -> ...ABCDEFGHIJKLMNOPQRSTUVXWYZ
SET2 -> ABCDEFGHIJKLMNOPQRSTUVXWYZABCDEFGHIJKLMNOPQRSTUVXWYZ

Discarding the initial sequence and the trailing repeating letters they become:

SET1 -> ABCDEFGHIJKLMNOPQRSTUVXWYZ
SET2 -> DEFGHIJKLMNOPQRSTUVXWYZABC

Which is the rot3 substitution.

Now for the second question:

How to make the second command work for both lower and upper case, just like the first command?

To make it work you put the desired number of dots at the beginning, matching your rot and 26 dots between to upper sequence and the lower sequence, just like this:

tr ........A-Z..........................a-z A-ZA-Za-za-z

This would successfully create an insensitive rot8. To visualize why this works let's see the sets:

SET1 -> ........ABCDEFGHIJKLMNOPQRSTUVXWYZ..........................abcdefghijklmnopqrstuvxwyz
SET2 -> ABCDEFGHIJKLMNOPQRSTUVXWYZABCDEFGHIJKLMNOPQRSTUVXWYZabcdefghijklmnopqrstuvxwyzabcdefghijklmnopqrstuvxwyz

Excluding the dots mapping and trailing letters:

SET1 -> ABCDEFGHIJKLMNOPQRSTUVXWYZabcdefghijklmnopqrstuvxwyz
SET2 -> IJKLMNOPQRSTUVXWYZABCDEFGHijklmnopqrstuvxwyzabcdefgh

Now it works for both upper and lower case :)

Another way to make it works is to use two tr commands as follow:

tr .............A-Z A-ZA-Z <<< "ABJ V hqrefgnaq" | tr .............a-z a-za-z

A caveat to using the dots substitution was gave by @iruvar: this command will not work as expected when the input string has dots. This is because the dots are being mapped to other letters and when doing the substitution, tr will change the input dot to the last mapped letter. But you can actually use any other character than dots. So, if using dots in your tr command is a problem, you can use @ instead, for example. This would work just as fine:

tr @@@@@@@@@@@@@A-Z A-ZA-Z <<< "GUNAX LBH NYY..."
  • 3
    I'm not sure how you concluded that "dots are replaced by a sequence of letters starting from a to the number of dots"; that's not at all the case. There's no magic involved; as Prvt_Yadv explained, there are two sets and tr is mapping from set 1 to set 2, as always, but in this case you've mapped the character . to A, and also B, and also ..., and also M. This doesn't matter since your input doesn't contain a ., but if it did it would turn into an M (tr uses the last output you specify for a given input) – Michael Mrozek Apr 6 at 6:33
  • Thanks for the clarification. I've updated the answer to fix my mistakes :) – Frederico Oliveira Apr 6 at 7:35
  • 1
    I admire your (initial) attempts to use science to determine the behaviour of this program, but in future you should come up with as many different hypotheses as possible and design tests to distinguish between all of them before trying to use them. Otherwise you end up getting confused, and adapting your model in a least-changes manner to explain subsequent results until your model's just a mass of special-cases. – wizzwizz4 Apr 6 at 18:46

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.