⇐ Previous | Next ⇒

# 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/" )
``````

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
``````