попытка сделать rot13-подобное решение, которое может обрабатывать специальные символы

Я хочу реализовать функцию rot13, которая использует случайно сгенерированные буквенно-цифровые клавиши, но я также хочу включить специальные символы и, похоже, не могу заставить это работать. Эта функция с использованием команды tr, включая специальные символы, не работает:

echo "$@" | tr "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789!@#$%^&*()-_=+\|" "A*KWx#o(5I-j\e|Hv)X7R0SJ=8+tBE^ucOVgMd2l$QkafU4nqz36LPhGb1rmswi%FC@!D_p9N&YyZT"

Поэтому я написал гораздо более запутанную функцию, которая разбивает буквы слова на массив, а затем передает каждый элемент массива через цикл for, состоящий из 92 операторов if/elif, которые при совпадении символа запускают подпрограмму замены sed:

conv=""
x="$1" ; echo "\$1: ${1}"
i=0
while [ $i -lt ${#x} ]; do y[$i]=${x:$i:1};  i=$((i+1));done
for f in "${y[@]}" ; do
    newF=$(echo "$f"|if [[ "$f" == "a" ]] ; then sed -r 's/a/7/g' ; elif [[ "$f" == "A" ]] ; then sed -r 's/A/\?/g' ; elif [[ "$f" == "b" ]] ; then sed -r 's/b/v/g' ; elif [[ "$f" == "B" ]] ; then sed -r 's/B/\./g' ; elif [[ "$f" == "c" ]] ; then sed -r 's/c/q/g' ; elif [[ "$f" == "C" ]] ; then sed -r 's/C/2/g' ; elif [[ "$f" == "d" ]] ; then sed -r 's/d/m/g' ; elif [[ "$f" == "D" ]] ; then sed -r 's/D/\Z/g' ; elif [[ "$f" == "e" ]] ; then sed -r 's/e/S/g' ; elif [[ "$f" == "E" ]] ; then sed -r 's/E/y/g' ; elif [[ "$f" == "f" ]] ; then sed -r 's/f/d/g' ; elif [[ "$f" == "F" ]] ; then sed -r 's/F/\)/g' ; elif [[ "$f" == "g" ]] ; then sed -r 's/g/z/g' ; elif [[ "$f" == "G" ]] ; then sed -r 's/G/K/g' ; elif [[ "$f" == "h" ]] ; then sed -r 's/h/T/g' ; elif [[ "$f" == "H" ]] ; then sed -r 's/H/\{/g' ; elif [[ "$f" == "i" ]] ; then sed -r 's/i/8/g' ; elif [[ "$f" == "I" ]] ; then sed -r 's/I/H/g' ; elif [[ "$f" == "j" ]] ; then sed -r 's/j/p/g' ; elif [[ "$f" == "J" ]] ; then sed -r 's/J/A/g' ; elif [[ "$f" == "k" ]] ; then sed -r 's/k/@/g' ; elif [[ "$f" == "K" ]] ; then sed -r 's/K/R/g' ; elif [[ "$f" == "l" ]] ; then sed -r 's/l/W/g' ; elif [[ "$f" == "L" ]] ; then sed -r 's/L/9/g' ; elif [[ "$f" == "m" ]] ; then sed -r 's/m/s/g' ; elif [[ "$f" == "M" ]] ; then sed -r 's/M/\(/g' ; elif [[ "$f" == "n" ]] ; then sed -r 's/n/V/g' ; elif [[ "$f" == "N" ]] ; then sed -r 's/N/t/g' ; elif [[ "$f" == "o" ]] ; then sed -r 's/o/\\/g' ; elif [[ "$f" == "O" ]] ; then sed -r 's/O/\!/g' ; elif [[ "$f" == "p" ]] ; then sed -r 's/p/=/g' ; elif [[ "$f" == "P" ]] ; then sed -r 's/P/n/g' ; elif [[ "$f" == "q" ]] ; then sed -r 's/q/#/g' ; elif [[ "$f" == "Q" ]] ; then sed -r 's/Q/e/g' ; elif [[ "$f" == "r" ]] ; then sed -r 's/r/g/g' ; elif [[ "$f" == "R" ]] ; then sed -r 's/R/f/g' ; elif [[ "$f" == "s" ]] ; then sed -r 's/s/-/g' ; elif [[ "$f" == "S" ]] ; then sed -r 's/S/0/g' ; elif [[ "$f" == "t" ]] ; then sed -r 's/t/,/g' ; elif [[ "$f" == "T" ]] ; then sed -r 's/T/:/g' ; elif [[ "$f" == "u" ]] ; then sed -r 's/u/_/g' ; elif [[ "$f" == "U" ]] ; then sed -r 's/U/Q/g' ; elif [[ "$f" == "v" ]] ; then sed -r 's/v/i/g' ; elif [[ "$f" == "V" ]] ; then sed -r 's/V/k/g' ; elif [[ "$f" == "w" ]] ; then sed -r 's/w/w/g' ; elif [[ "$f" == "W" ]] ; then sed -r 's/W/l/g' ; elif [[ "$f" == "x" ]] ; then sed -r 's/x/3/g' ; elif [[ "$f" == "X" ]] ; then sed -r 's/X/\]/g' ; elif [[ "$f" == "y" ]] ; then sed -r 's/y/5/g' ; elif [[ "$f" == "Y" ]] ; then sed -r 's/Y/O/g' ; elif [[ "$f" == "z" ]] ; then sed -r 's/z/F/g' ; elif [[ "$f" == "Z" ]] ; then sed -r 's/Z/"/g' ; elif [[ "$f" == "0" ]] ; then sed -r 's/0/;/g' ; elif [[ "$f" == "1" ]] ; then sed -r 's/1/E/g' ; elif [[ "$f" == "2" ]] ; then sed -r 's/2/>/g' ; elif [[ "$f" == "3" ]] ; then sed -r 's/3/u/g' ; elif [[ "$f" == "4" ]] ; then sed -r 's/4/\$/g' ; elif [[ "$f" == "5" ]] ; then sed -r 's/5/</g' ; elif [[ "$f" == "6" ]] ; then sed -r 's/6/\+/g' ; elif [[ "$f" == "7" ]] ; then sed -r 's/7/x/g' ; elif [[ "$f" == "8" ]] ; then sed -r 's/8/L/g' ; elif [[ "$f" == "9" ]] ; then sed -r 's/9/C/g' ; elif [[ "$f" == "!" ]] ; then sed -r 's/\!/a/g' ; elif [[ "$f" == "@" ]] ; then sed -r 's/@/\//g' ; elif [[ "$f" == "#" ]] ; then sed -r 's/#/M/g' ; elif [[ "$f" == "$" ]] ; then sed -r "s/\$/'/g" ; elif [[ "$f" == "%" ]] ; then sed -r 's/%/1/g' ; elif [[ "$f" == "^" ]] ; then sed -r 's/\^/c/g' ; elif [[ "$f" == "&" ]] ; then sed -r 's/\&/h/g' ; elif [[ "$f" == "*" ]] ; then sed -r 's/\*/U/g' ; elif [[ "$f" == "(" ]] ; then sed -r 's/\(/\|/g' ; elif [[ "$f" == ")" ]] ; then sed -r 's/\)/\[/g' ; elif [[ "$f" == "-" ]] ; then sed -r 's/-/I/g' ; elif [[ "$f" == "_" ]] ; then sed -r 's/_/\*/g' ; elif [[ "$f" == "=" ]] ; then sed -r 's/=/G/g' ; elif [[ "$f" == "+" ]] ; then sed -r 's/\+/P/g' ; elif [[ "$f" == "|" ]] ; then sed -r 's/\|/o/g' ; elif [[ "$f" == '\' ]] ; then sed -r 's/\\/Y/g' ; elif [[ "$f" == "[" ]] ; then sed -r 's/\[/j/g' ; elif [[ "$f" == "{" ]] ; then sed -r 's/\{/B/g' ; elif [[ "$f" == "]" ]] ; then sed -r 's/\]/\%/g' ; elif [[ "$f" == "}" ]] ; then sed -r 's/\}/J/g' ; elif [[ "$f" == ";" ]] ; then sed -r 's/;/X/g' ; elif [[ "$f" == ":" ]] ; then sed -r 's/:/\^/g' ; elif [[ "$f" == "'" ]] ; then sed -r "s/'/D/g"; elif [[ "$f" == '"' ]] ; then sed -r 's/"/\}/g' ; elif [[ "$f" == "," ]] ; then sed -r 's/,/4/g' ; elif [[ "$f" == "<" ]] ; then sed -r 's/</r/g' ; elif [[ "$f" == "." ]] ; then sed -r 's/\./N/g' ; elif [[ "$f" == ">" ]] ; then sed -r 's/>/\&/g' ; elif [[ "$f" == "/" ]] ; then sed -r 's/\//6/g' ; elif [[ "$f" == "?" ]] ; then sed -r 's/\?/b/g' ; fi)
    echo "converting: $f to $newF"
    conv="${conv}${newF}" ; echo "\$conv: ${conv}"
done

Сейчас вроде работает, но не совсем. Мне нужно ввести входные слова для преобразования, заключенные в одинарные кавычки, из-за специальной обработки символов, кажется (много проб и ошибок, чтобы понять это!), Что все в порядке, кроме как ввести слово для преобразования если он содержит символ одинарной кавычки?

В конечном счете, я бы предпочел использовать более простое решение, такое как команда tr, если у кого-нибудь есть совет, как управлять специальными символами с помощью tr, это было бы здорово. Если нет, то как я могу ввести слово, содержащее специальные символы, включая одинарную кавычку, если кажется, что ввод должен быть заключен в одинарные кавычки?

Может юникоды? Но это звучит как еще более уродливое решение, чем то, что у меня уже есть.

Дополнительная информация: Привет, Джин, спасибо за быстрые ответы. Это почти там, намного дальше, чем мне удалось. Я полностью забыл о тире, но обычно я думаю о них только тогда, когда заключаю их в квадратные скобки, помня, что они должны быть последними указанными символами, чтобы избежать определения нежелательного диапазона.

Все переведенные буквы, идущие после первого экранированного символа во втором (переведенном) наборе символов, тире - добавляют смещение; и каждый последующий экранированный после этого char добавляет еще одно смещение к результату. Таким образом, для:

$ echo 'h3!10 w()rLd'|tr 'aAbBcCdDeEFGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789!@#$%^&*()\-_=+\\|' 'A*KWx#o(5I\-j\\e|Hv)X7R0SJ=8+tBE^ucOVgMd2l$QkafU4nqz36LPhGb1rmswi%FC@!D_p9&YyT'

результат:

\Ps63 kD_c0o

но должно быть:

|5iPL fp9VJo
Я по ошибке указал "е" вместо "3", так что сделайте так:

|GiPL fp9VJo

Все на два места. Вероятно, я мог бы придумать хак, чтобы вычислить экранированные символы и соответствующим образом настроить, но кажется странным, что включение специальных символов должно быть настолько сложным. Я даже пытался поместить их в элементы массива, что тоже было бы неплохо, но bash отказывается от этого.

Я использовал электронную таблицу, чтобы сделать X-ссылки на символы как можно проще. Но вот они рядом:

a <=> A   n <=> +   0 <=> L  
A <=> *   N <=> t   1 <=> P  
b <=> K   o <=> B   2 <=> h  
B <=> W   O <=> E   3 <=> G  
c <=> x   p <=> ^   4 <=> b  
C <=> #   P <=> u   5 <=> 1  
d <=> o   q <=> c   6 <=> r  
D <=> (   Q <=> O   7 <=> m  
e <=> 5   r <=> V   8 <=> s  
E <=> I   R <=> g   9 <=> w  
f <=> -   s <=> M   ! <=> i  
F <=> j   S <=> d   @ <=> %  
g <=> \   t <=> 2   # <=> F  
G <=> e   T <=> l   $ <=> C  
h <=> |   u <=> $   % <=> @  
H <=> H   U <=> Q   ^ <=> !  
i <=> v   v <=> k   & <=> D  
I <=> )   V <=> a   * <=> _  
j <=> X   w <=> f   ( <=> p  
J <=> 7   W <=> U   ) <=> 9  
k <=> R   x <=> 4   - <=> N  
K <=> 0   X <=> n   _ <=> &  
l <=> S   y <=> q   = <=> Y  
L <=> J   Y <=> z   + <=> y  
m <=> =   z <=> 3   | <=> Z  
M <=> 8   Z <=> 6   \ <=> T

Еще больше информации: поэтому я пошел другим, возможно, более неэффективным путем, но теперь все работает нормально; за исключением того, что по-прежнему нет способа ввести строку, содержащую как одинарные, так и двойные кавычки, поэтому, если нет способа обойти то, что я еще не нашел, мне просто нужно помнить об этом ограничении при использовании следующий сценарий:

# array containing regular alphaNumSpecChar
abc=(a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 '!' '@' '#' '$' '%' '^' '&' '*' '(' ')' '-' '_' '=' '+' '|' '\' '[' '{' ']' '}' ';' ':' "'" '"' ',' '.' '/' '?' '<' '>') #; echo "${#abc[@]}"

# randomly generated array key to cross-reference array elements in "$abc[@]}", so that if user input charater being processed is "b", "${abc[1]}" then the value for "${ranNum[2]}", 41 is used to convert "b" to "${abc[41]}", which is "P" 
ranNum=(40 41 47 52 1 68 20 54 17 2 59 13 11 57 90 33 82 4 31 70 29 26 83 63 56 38 28 61 25 32 49 43 23 45 64 55 9 69 44 60 91 5 84 88 22 14 62 87 7 86 39 78 48 46 58 73 3 6 16 8 37 72 74 67 80 35 77 66 89 53 12 79 42 27 21 18 65 0 75 85 34 10 15 19 76 50 51 71 81 36 24 30)

# var for conversion result
convRes=""

# while loop to map user's input string's individual chars to array 'y' 
x="$1" #; echo "\$1: ${1}"
i=0
while [ $i -lt ${#x} ]; do
    y[$i]=${x:$i:1}
    i=$((i+1))
done


for f in "${y[@]}" ; do     # for loop to process each array element from user's input string
    cntr1=0
    while [[ "$cntr" -le "${#abc[@]}" ]] ; do   # while loop to cycle thru all elements in array "${abc[@]}" to figure out what char it is
        if [[ "$f" == "${abc[$cntr1]}" ]] ; then        #'if' user input char is matched then using value of "$cntr1" var cross reference applicable random char conversion number
            convNum="${ranNum[$cntr1]}" #; echo "\$convNum: ${convNum}"
            #echo "converting: $f to $convNum"
            # append current converted char to $convRes var
            convRes="${convRes}${abc[$convNum]}" #; echo "\$convRes: ${convRes}"
            break
        fi
        ((cntr1++))
    done
done

echo -e "\n\$convRes: ${convRes}\n"

Итак, если пользователь вводит:

$ rotateRandom.sh 'h3!1()W0rLd'

скрипт возвращает:

$convRes: 2_b@m{hWe*0

Результат, на который я изначально надеялся использовать tr, и если бы его можно было получить, используя это гораздо более простое решение, буква за буквой, чем включает специальные символы, я бы использовал его, но все мои попытки заставить tr правильно отображать я не увенчались успехом. Может быть, я просто просматривал и редактировал одну и ту же команду слишком долго и слишком много раз, чтобы увидеть простое решение.


person nanker    schedule 24.05.2014    source источник
comment
Попробуйте одинарные кавычки вокруг строк tr. В противном случае вы должны экранировать специальные символы с помощью обратной косой черты. Вы сделали это в других строках! Команда tr будет работать нормально, если вы укажете правильные аргументы.   -  person Gene    schedule 24.05.2014
comment
@Gene На самом деле я играл с этим, пока моя голова не почувствовала, что она взорвется. Я пробовал как одинарные, так и двойные кавычки; Я только что попытался избежать специальных символов, как вы предложили, снова используя как одинарные, так и двойные кавычки, но все равно без любви. Кажется, что строчные буквы всегда переводятся нормально (как и ожидалось), но прописные буквы имеют тенденцию либо переводиться в какой-то неожиданный символ, либо один и тот же символ для каждой прописной буквы. Очень странно и сводит меня с ума. Не возражаете ли вы скопировать tr, который я включил, и запустить в cli, чтобы посмотреть, работает ли он у вас?   -  person nanker    schedule 24.05.2014
comment
Я исправил проблемы. См. статью ниже.   -  person Gene    schedule 24.05.2014


Ответы (2)


Проблема в том, что вам нужны одинарные кавычки вокруг аргументов tr, чтобы предотвратить расширение bash $ в строках. Кроме того, вы забыли, что tr специально обрабатывает несколько символов:

  • Дефис - создает наборы символов.
  • Обратная косая черта экранирует все, что следует за ней.

Это устраняет эти проблемы с помощью обратной косой черты, экранирующей тире и обратную косую черту.

tr 'aAbBcCdDeEFGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789!@#$%^&*()\-_=+\\|' \
   'A*KWx#o(5I\-j\\e|Hv)X7R0SJ=8+tBE^ucOVgMd2l$QkafU4nqz36LPhGb1rmswi%FC@!D_p9&YyT' 

У меня mingw bash работает нормально.

Вы не использовали квадратные скобки [, но если вы будете использовать их в будущем, знайте, что они тоже могут вызвать проблемы, если их не экранировать.

Дополнение

Что касается утверждения ОП о том, что это неверно, я не понимаю, как это сделать. Используя экранированные строки выше, но удаляя обратную косую черту, чтобы показать эквивалентность:

Plain:  aAbBcCdDeEFGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789!@#$%^&*()-_=+\|
Cypher: A*KWx#o(5I-j\e|Hv)X7R0SJ=8+tBE^ucOVgMd2l$QkafU4nqz36LPhGb1rmswi%FC@!D_p9&YyT

Перевод вручную:

h3!10 w()rLd
\Ps63 kD_c0o

Тем не менее, строка «должно быть» в ОП - это нечто другое. Может быть, я не понимаю, чего мы пытаемся достичь.

Внедрение новой таблицы OP

Ваша новая таблица — это не то, что у вас есть в командной строке tr. Вот соответствующий перевод (который я получил из небольшого скрипта Ruby с вашей таблицей в качестве входных данных):

Plain:  aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789!@#$%^&*()-_=+|\
Cypher: A*KWx#o(5I-j\e|Hv)X7R0SJ=8+tBE^ucOVgMd2l$QkafU4nqz36LPhGb1rmswi%FC@!D_p9N&YyZT

Обратите внимание, что это только ваша таблица, отображаемая горизонтально. Ничего особенного.

Результирующая команда tr с добавленными экранами обратной косой черты просто

echo 'h3!10 w()rLd' | \
tr 'aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789!@#$%^&*()\-_=+|\\' \
   'A*KWx#o(5I\-j\\e|Hv)X7R0SJ=8+tBE^ucOVgMd2l$QkafU4nqz36LPhGb1rmswi%FC@!D_p9N&YyZT'

На моей коробке это печатает:

|GiPL fp9VJo

Или переключите параметры, чтобы пойти в другом направлении (потому что ваш шифр не симметричен).

echo '|GiPL fp9VJo' | \
tr 'A*KWx#o(5I\-j\\e|Hv)X7R0SJ=8+tBE^ucOVgMd2l$QkafU4nqz36LPhGb1rmswi%FC@!D_p9N&YyZT' \
   'aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789!@#$%^&*()\-_=+|\\'

Это производит

h3!10 w()rLd

на моей коробке, как и следовало ожидать.

person Gene    schedule 24.05.2014
comment
добавил дополнительную информацию OP. - person nanker; 24.05.2014
comment
@nanker Я не понимаю, как вы получили h3!10 w()rLd перевод, который, как вы сказали, был правильным. Команда tr работает нормально. См. мое дополнение выше. - person Gene; 24.05.2014
comment
Я добавил таблицу с двумя наборами символов рядом, как они должны переводиться, иллюстрируя то, что мне кажется, что символы должны переводиться. Возможно, я ошибаюсь, я слишком долго смотрел на этот путь, я думаю, но таблица выглядит правильно, и если это так, то экранированные символы, похоже, вводят смещение. Теперь я думаю, что попытка манипулировать специальными символами — это гораздо большая головная боль, чем я когда-либо мог себе представить, поэтому я не удивлен. Ни один из способов, которые я пробовал, не был простым. - person nanker; 24.05.2014
comment
@nanker Думаю, тебе нужно перевести дух или немного поспать. Команда tr будет работать нормально, если вы дадите ей правильную таблицу перевода. Смотрите мое новое дополнение. - person Gene; 24.05.2014
comment
да, вы правы, команда tr теперь работает нормально, и это после некоторого сна. Я возился с таким количеством вариантов одинарных и двойных кавычек и escape-символов, что сравнивал неправильные символы. Блин! моя вина. Извините и спасибо за вашу настойчивость, потому что команда tr — это способ быть намного более кратким. - person nanker; 25.05.2014

Есть две проблемы с вашим исходным решением с «tr»:

  1. Вы используете двойные кавычки, но внутри есть символ $, который используется в оболочке.
  2. Ваш перевод несимметричен: a переводится в A, а A переводится в *.

Я бы, вероятно, сделал это с помощью sed -e 'y///' (или, может быть, python), но одно из преимуществ tr заключается в том, что вы можете использовать наборы символов:

tr '[:upper:][:lower:]' '[:lower:][:upper:]'

Но в любом случае вы должны сами перечислить специальные пары символов.

person o11c    schedule 24.05.2014
comment
+1 за два хороших очка. Но даже sun4 sed поддерживает наборы символов. и, что более важно, не будет ли tr 'ABC...abc...' 'abc...ABC...', эквивалентное вашему tr решению, просто изменить регистр? Мой tr говорит `...: неверная строка назначения. Извините, но +1-1=0 ;-/ . Удачи. - person shellter; 24.05.2014
comment
@ o11c обратился к 1. в моем комментарии выше. Перевод не должен быть симметричным, если я правильно понимаю. Я хочу перевести каждую букву из пользовательского ввода в случайный, загадочный результат. Тогда команда tr будет самым простым решением для изменения шаблона случайного перевода по желанию. - person nanker; 24.05.2014