2

I am meant to create a Caesar Cypher that takes in a parameter and shifts the code based on that parameter but my code messes up with the Upper Case and lower case.

So, it's meant to be like:

$ echo "I came, I saw, I conquered." | ./caesar.sh
V pnzr, V fnj, V pbadhrerq.

but I get:

V pnzr, V FnJ, V pBADHERrq.

My code is:

#!/bin/sh

if [ -z "$@" ];then 
    rotation=13;
else 
    rotation=$((@ % 16));
fi

tr $(printf %${rotation}s | tr ' ' '.')\a-zA-Z a-zA-Z

How can I fix this?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
xXxXXXXX
  • 11
  • 4
  • If someone passes more than one argument, you're unlikely to get the result you wanted. Should you rotate the upper-case letters in one `tr` command and the lower-case in another? Or do you just need to generate the mappings separately and combine them 'better'? Both the semicolons at the end of the line are unnecessary in a script; they would be necessary in a one-liner. – Jonathan Leffler Feb 26 '16 at 04:08
  • If you try to encode `CAN I SEE SYZYGY AT MIDNIGHT` (with the default shift of 13), you get a lot more `Z`s in the output than you really want (all upper-case letters in the second half of the alphabet end up as `Z`, which isn't good for the decipherability of the enciphered text). Was the modulo 16 meant to be modulo 26? – Jonathan Leffler Feb 26 '16 at 04:52
  • That is also the problem I have, the 16 was meant to be 26 – xXxXXXXX Feb 26 '16 at 13:17
  • The whole point of exercises such as this is to learn how to write the working code. You fix this by debugging (print statements, tracing execution), studying documentation, studying the problem and thinking. Learning how to "get code working" is a very important step to becoming a productive developer. – zaph Feb 26 '16 at 16:40

2 Answers2

2

You are rotating across the entire double-alphabet, 'a-zA-Z', so 's' maps to 'F':

abcdefghijklmnopqrstuvwxyzABCDEFGHIKLMNOPQRSTUVWXYZ
                  |------------^

You apparently want to preserve case, so I would recommend that you apply two separate mappings: first, map 'a-z' to 'n-za-m' (or whatever, as appropriate for your input parameter). Then in the second pass, map capitals, 'A-Z' -> 'N-ZA-M'.

aghast
  • 14,785
  • 3
  • 24
  • 56
  • So this should work: #!/bin/sh if [ -z "$@" ];then rotation=13; else rotation=$((@ % 26)); fi tr $(printf %${rotation}s | tr ' ' '.')'A-Z' 'A-ZA-Z' tr $(printf %${rotation}s | tr ' ' '.')'a-z' 'a-za-z' – xXxXXXXX Feb 26 '16 at 13:14
  • I chnaged my code to # Hello World Program in Bash Shell #!/bin/sh IN="It is better to create than to learn! Creating is the essence of life." if [ -z "$@" ];then rotation=14; else rotation=$((@ % 26)); fi echo $IN | tr $(printf %${rotation}s | tr ' ' '.')'a-zA-Z' 'a-za-zA-ZA-Z' which works, but changes the period. How can I fix that – xXxXXXXX Feb 26 '16 at 14:30
  • Pick a character other than period? Or figure out which character it maps to, and use another `tr` to map it back by itself. – aghast Feb 26 '16 at 15:15
1

A basic adaptation of your scheme that works is:

rotation=$((${1:-13} % 26))
padding=$(printf "%${rotation}s" "" | tr ' ' '\001')
tr "${padding}a-z" "a-za-z" |
tr "${padding}A-Z" "A-ZA-Z"

This uses parameter expansion and arithmetic to determine the rotation.

It uses your basic mechanism for setting the padding, but uses Control-A instead of . as the padding character; you seldom have Control-A in your text.

The actual rotation commands deal with lower case separately from upper case.

With the script contained in a file script.sh, I got:

$ bash script.sh
I came, I saw, I conquered
Can you say SYZYGY after midnight?
V pnzr, V fnj, V pbadhrerq
Pna lbh fnl FLMLTL nsgre zvqavtug?
$ bash script.sh 3
I came, I saw, I conquered, and O, was it ever worthwhile!
Can you say SYZYGY after midnight?  ABC...XYZ abc...xyz
L fdph, L vdz, L frqtxhuhg, dqg R, zdv lw hyhu zruwkzkloh!
Fdq brx vdb VBCBJB diwhu plgqljkw?  DEF...ABC def...abc
$

The pipeline meant that the first line of input was not pushed through to the second tr command at the end of line.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278