Caesar Cypher in Bash Oneliner
Post created 2013-12-07 16:57 by Gabe Koss.
I got this problem from karans Project repository. The challenge is as follows:
Caesar cipher - Implement a Caesar cipher, both encoding and decoding. The key is an integer from 1 to 25. This cipher rotates the letters of the alphabet (A to Z). The encoding replaces each letter with the 1st to 25th next letter in the alphabet (wrapping Z to A). So key 2 encrypts "HI" to "JK", but key 20 encrypts "HI" to "BC". This simple "monoalphabetic substitution cipher" provides almost no security, because an attacker who has the encoded message can either use frequency analysis to guess the key, or just try all 25 keys.
Mimimal Solution
Here is the tightest and most 'one-liney' I could make this:
# Sample usage:
# ./bash_caesar <key (1-25)> <input file>
tr 'A-Z' 'a-z' < $2 | tr 'a-z' $( echo {a..z} | sed -r 's/ //g' | sed -r "s/(.{$1})(.*)/\2\1/" )
Readable Solution
Here is some very similar logic split out into a couple lines to make it more understandable:
#!/bin/bash
# example usage:
# ./bash_caesar <key (1-25)> <input file>
#
# [email protected]:~$ echo "hi" > sample
# [email protected]:~$ ./bash_caesar.sh 2 sample
# jk
# [email protected]:~$ ./bash_caesar.sh 20 sample
# bc
export A=$(echo {a..z} | sed -r 's/ //g')
export C=$(echo $A | sed -r "s/^.{$1}//g")$(echo $A | sed -r "s/.{$( expr 26 - $1 )}$//g")
tr '[A-Z]' $A < $2 | tr $A $C
Explanation
Inputs
This wants to be written as script and called with two arguments:
- The key (
$1
) should be a numeric value between 0-26. Realistically the keyspace is 1-25 but 0 and 26 both work as a complete rotation. if you go over problems you get into trouble with the negative numbers being passed into a regex. - The input file (
$2
) should be the path to a text file
Alphabet string
I store a variable $A (for alphabet!) which is a string
export A=$(echo {a..z} | sed -r 's/ //g';);
This string looks like abcdefghijklmnopqrstuvwxyz
. The trick is that echo
{a..z}
returns with spaces between each character like a b c ...
and so
these spaces are stripped out with sed
.
Cypher
The cypher is created by rotating the alpahbet string a the number of
characters indicated by the key. This is accomplished by passing the Alphabet
variable $A
through two different transforms with `sed.
Remove $1
characters
echo $A | sed -r "s/^.{$1}//g"
This deletes all characters in the space defined by the key $1
. For example
if the key is 2 this will return cdefghijklmnopqrstuvwxyz
.
Append 26 - $1
characters
echo $A | sed -r "s/.{$( expr 26 - $1 )}$//g"
This deletes all characters after the number defined by the key $1
. For
example if the key is 2 this will return ab
.
Translate file with Cypher
The actual translation is done with two chained tr
commands.
Downcase the file
tr '[A-Z]' $A < $2
In the first tr
command the input file $2
is translated by replacing any
capital letter (in the range '[A-Z]'
) with the corresponding value in the
alphabet variable $A
Translate the string
tr $A $C
Finally the downcased string is passed to tr
a second time and all characters
in the alphabet $A
are replaced by the corresponding value in the cypher $C
Decoder
The example here only does encoding. The decoder for this can be created by
swapping the $A
and $C
variables in the final `tr:
tr $C $A
comments powered by Disqus