アルファベット順にn文字ずらした文字列を生成する、いわゆるシーザー暗号(Caesar cipher)。
これをLinuxコンソール上でワンライナーで作成させるには、どうすればよいのだろう?と思ったので、少し調べてみた。
とりあえず、以下のようにコマンドを実行することでアルファベットの小文字を任意の数ずらすことが可能だ。
(○にずらす数を入れる)
コマンド | tr $(printf %○s | tr ' ' '.' | tac)\a-z a-za-z # アルファベットをずらす
コマンド | tr a-z $(printf %○s | tr ' ' '.')\a-z # ずらしたのを戻す
[root@BS-PUB-CENT7-01 ~]# echo abcdefg | tr $(printf %1s | tr ' ' '.' | tac)\a-z a-za-z
bcdefgh
[root@BS-PUB-CENT7-01 ~]# echo abcdefg | tr $(printf %5s | tr ' ' '.' | tac)\a-z a-za-z
fghijkl
[root@BS-PUB-CENT7-01 ~]# echo abcdefg | tr $(printf %9s | tr ' ' '.' | tac)\a-z a-za-z
jklmnop
[root@BS-PUB-CENT7-01 ~]# echo jklmnop | tr a-z $(printf %9s | tr ' ' '.')\a-z
abcdefg
大文字も含める場合は、現時点だと後ろにもう一個trを含める方法しか思いつかなかった。
[root@BS-PUB-CENT7-01 ~]# echo aBCdEfg | tr $(printf %9s | tr ' ' '.' | tac)\a-z a-za-z | tr $(
printf %9s | tr ' ' '.' | tac)\A-Z A-ZA-Z
jKLmNop
なお、ROT13(13個ずらす)場合は、Pythonのライブラリがあるのでそちらで処理が可能だ。
コマンド | python -c 'print(raw_input().encode("rot13"))'
[root@BS-PUB-CENT7-01 ~]# echo ABcdEfGh | python -c 'print(raw_input().encode("rot13"))'
NOpqRsTu
2016/06/27 追記
他の人が考えてくれた方法があったので追記。
Linuxコンソール上でアルファベット順にn文字ずらした文字列を得る(シーザー暗号) th0x0472.log/ウェブリブログ(@th0x0472)
シーザー暗号 #シェル芸 Just another Ruby porter, 2016-6-c(@eban)
特に、某界隈(だけでなく)で有名なebanさんの解き方が…
こんな短くできたんだ…
tacの意味がよくわからない。あとtrで空白を"."に変換しているのも不要のような。
$ echo abcdefg | tr "$(printf %1sA-z)" A-zA-z
bcdefgh
$ echo aBCdEfg | tr "$(printf %9sA-z)" A-zA-z
jKLmNopでいいのではないかな。大文字が含まれていてもいける。
$ echo bcdefgh | tr A-z "$(printf %1sA-z)"
abcdefg
$ echo jKLmNop | tr A-z "$(printf %9sA-z)"
aBCdEfg
空白でいけたとは…
復号化も簡単にかけられる様子。
[root@BS-PUB-CENT7-01 ~]# # 暗号化
[root@BS-PUB-CENT7-01 ~]# echo aBcDeFg | tr "$(printf %1sA-z)" A-zA-z
bCdEfGh
[root@BS-PUB-CENT7-01 ~]# # 復号化
[root@BS-PUB-CENT7-01 ~]# echo aBcDeFg | tr "$(printf %1sA-z)" A-zA-z | tr A-z "$(printf %1sA-z)"
aBcDeFg
2016/06/28 追記
シーザー暗号 #シェル芸 Just another Ruby porter, 2016-6-c(@eban)
@ebanさんの昨日の方法だと、A-zがA-Za-zという扱いになるので、Zを一文字ずらすと小文字のaになってしまい想定の動作にならないというのがあったらしい。
で、さらに改良が加えられていた。
昨日のはzがAになったりといろいろとダメダメだったの出直し。
1つずらす場合は
tr a-zA-Z b-zaB-ZA
となればいいわけだが、1からこの文字列を作るのは難しい。そこで
abcdefghijklmnopqrstuvwxyz
を
bcdefghijklmnopqrstuvwxyza
に変換することにする。これはsedなら簡単だ。
$ printf %s {a..z} | sed -E 's/^(.{1})(.*)/\2\1/;s/.*/&\U&/'
bcdefghijklmnopqrstuvwxyzaBCDEFGHIJKLMNOPQRSTUVWXYZA
というわけで大文字も含んだ形で完成。
$ echo aBCdEfgXyZ | tr a-zA-Z "$(printf %s {a..z} | sed -E 's/^(.{9})(.*)/\2\1/;s/.*/&\U&/')"
jKLmNopGhI
$ echo jKLmNopGhI | tr "$(printf %s {a..z} | sed -E 's/^(.{9})(.*)/\2\1/;s/.*/&\U&/')" a-zA-Z
aBCdEfgXyZ
でも、ちょっとださい。
そんな方法が…
上の@ebanさんの方法、sedで置換するアルファベットの順番を入れ替えてるのがキモなので、自分の中に落とし込むために整理する。
[test@BS-PUB-CENT7-01 ~]$ # printfでa~zまでを出力させる。
[test@BS-PUB-CENT7-01 ~]$ # ※echoでブレース展開すると間に空白が入るので、printfで出している(ここでは最後に改行が入るためのechoしている)。
[test@BS-PUB-CENT7-01 ~]$ echo $(printf %s {a..z})
abcdefghijklmnopqrstuvwxyz
[test@BS-PUB-CENT7-01 ~]$
[test@BS-PUB-CENT7-01 ~]$ # sedでアルファベットの位置を入れ替えている。「^(.{3})」で、行頭から数えて○文字(とりあえず3文字にしている)を指定している。
[test@BS-PUB-CENT7-01 ~]$ # このとき、拡張正規表現で()内で指定しているものは、\1、\2で指定でき るので、それの位置を入れ替えてやる。
[test@BS-PUB-CENT7-01 ~]$ echo $(printf %s {a..z}) | sed -E 's/^(.{3})(.*)/\2\1/'
defghijklmnopqrstuvwxyzabc
[test@BS-PUB-CENT7-01 ~]$ echo $(printf %s {a..z}) | sed -E 's/^(.{3})(.*)/\2/'
defghijklmnopqrstuvwxyz
[test@BS-PUB-CENT7-01 ~]$ echo $(printf %s {a..z}) | sed -E 's/^(.{3})(.*)/\1/'
abc
[test@BS-PUB-CENT7-01 ~]$
[test@BS-PUB-CENT7-01 ~]$ # アルファベットの位置を入れ替えたものに対し、大文字変換したものを追 加する。
[test@BS-PUB-CENT7-01 ~]$ echo $(printf %s {a..z}) | sed -E 's/^(.{3})(.*)/\1/;s/.*/&\U&/'
abc<strong>ABC</strong>
[test@BS-PUB-CENT7-01 ~]$ echo $(printf %s {a..z}) | sed -E 's/^(.{3})(.*)/\2\1/;s/.*/&\U&/'
defghijklmnopqrstuvwxyzabcDEFGHIJKLMNOPQRSTUVWXYZABC