Скрипт Bash для получения значений ASCII для алфавита

Ответы:

70

Определите эти две функции (обычно доступные на других языках):

chr() {
  [ "$1" -lt 256 ] || return 1
  printf "\\$(printf '%03o' "$1")"
}

ord() {
  LC_CTYPE=C printf '%d' "'$1"
}

Использование:

chr 65
A

ord A
65
dsmsk80
источник
7
@ dmsk80: +1. Для других , как я , которые думают , что они обнаружили опечатку: "'A"правильно тогда , если вы используете "A"это будет сказать: A: invalid number. Кажется, это сделано на стороне printf (то есть в оболочке "'A"действительно 2 символа, a 'и a A. Они передаются в printf. А в контексте printf он преобразуется в значение ascii A, (и, наконец, печатается как десятичное число благодаря '%d'. Используйте, 'Ox%x'чтобы показать его в гекса или '0%o'чтобы иметь его в восьмеричном))
Olivier Dulac
3
-1 для не объясняя , как это работает ... шутит: D, но серьезно , что делает это printf "\\$(printf '%03o' "$1")", '%03o', LC_CTYPE=Cи апостроф в "'$1"делах?
Раззак
1
Прочитайте все детали в FAQ 71 . Отличный детальный анализ.
19

Вы можете увидеть весь набор с:

$ man ascii

Вы получите таблицы в восьмеричном, шестнадцатеричном и десятичном виде.

брод
источник
Есть также пакет ascii для дистрибутивов на основе Debian, но (по крайней мере, сейчас) вопрос помечен как bash, так что это не поможет OP. На самом деле, он установлен в моей системе, и все, что я получаю от man ascii, это его справочная страница.
Джо
12

Если вы хотите расширить его до символов UTF-8:

$ perl -CA -le 'print ord shift' 😈
128520

$ perl -CS -le 'print chr shift' 128520
😈

С bash, kshили zshвстроенные команды:

$ printf "\U$(printf %08x 128520)\n"
😈
Стефан Шазелас
источник
Вы намеревались поместить символ квадратного прямоугольника, иначе оригинальный символ не отображается в сообщении и заменяется символом квадратного прямоугольника.
MTK
1
@mtk, вам нужен браузер, который отображает UTF-8 и шрифт, который имеет этот символ 128520 .
Стефан Шазелас
Я использую последний Chrome и не думаю, что он не поддерживает UTF-8. Хотите знать, в каком браузере вы находитесь?
MTK
@mtk, iceweaselна Debian sid. Шрифт, подтвержденный веб-консолью iceweasel, называется «DejaVu Sans», и у меня установлены пакеты ttf-dejavu ttf-dejavu-core ttf-dejavu-extra, которые поступают из Debian с апстримом
Стефан
что такое база 128520? мой собственный , ctbl()кажется, правильно дать мне возможность показать его и нарезать полукокс из головы колонны с printf, но она ставит 4*((o1=360)>=(d1=240)|(o2=237)>=(d2=159)|(o3=230)>=(d3=152)|(o4=210)>=(d4=136))в $OPTARGдля байтовых значений.
mikeserv
12

Это хорошо работает,

echo "A" | tr -d "\n" | od -An -t uC

echo "A"                              ### Emit a character.
         | tr -d "\n"                 ### Remove the "newline" character.
                      | od -An -t uC  ### Use od (octal dump) to print:
                                      ### -An  means Address none
                                      ### -t  select a type
                                      ###  u  type is unsigned decimal.
                                      ###  C  of size (one) char.

в точности эквивалентно:

echo -n "A" | od -An -tuC        ### Not all shells honor the '-n'.
Saravanan
источник
3
Можете ли вы добавить небольшое объяснение?
Бернхард
tr, чтобы удалить "\ n" (новую строку) из ввода. od используется для -t dC для печати в десятичном формате.
Сараванан
1
echo -nподавляет завершающий tr -d "\n"
перевод
2
@Gowtham, только с некоторыми реализациями, например echo, не в Unix-совместимом эхо. printf %s Aбудет портативным.
Стефан Шазелас
6

Я собираюсь для простого (и элегантного?) Решения Bash:

for i in {a..z}; do echo $(printf "%s %d" "$i" "'$i"); done

В сценарии вы можете использовать следующее:

CharValue="A"
AscValue=`printf "%d" "'$CharValue"

Обратите внимание на одинарную кавычку перед CharValue. Обязан ...

phulstaert
источник
1
Чем ваш ответ отличается от ответа dsmsk80?
Бернхард
1
Моя интерпретация вопроса: «Как получить значения ASCII для значений алфавита». Не то, как определить функцию для получения значения ASCII для одного символа. Итак, мой первый ответ - короткая однострочная команда для получения значений ASCII для алфавита.
phulstaert
Я понимаю вашу точку зрения, но все же думаю, что суть обоих ответов такова printf "%d".
Бернхард
2
Я согласен, что это важная часть процесса для достижения результата, но я не хотел делать предположение, что xmpirate знал о «для меня в» и использовании диапазона. Если бы он хотел список, это могло бы сэкономить время ;-). Кроме того, будущие читатели могут найти мои дополнения полезными.
phulstaert
6
ctbl()  for O                   in      0 1 2 3
        do  for o               in      0 1 2 3 4 5 6 7
                do for  _o      in      7 6 5 4 3 2 1 0
                        do      case    $((_o=(_o+=O*100+o*10)?_o:200)) in
                                (*00|*77) set   "${1:+ \"}\\$_o${1:-\"}";;
                                (140|42)  set   '\\'"\\$_o$1"           ;;
                                (*)       set   "\\$_o$1"               ;esac
                        done;   printf   "$1";   shift
                done
        done
eval '
ctbl(){
        ${1:+":"}       return "$((OPTARG=0))"
        set     "" ""   "${1%"${1#?}"}"
        for     c in    ${a+"a=$a"} ${b+"b=$b"} ${c+"c=$c"}\
                        ${LC_ALL+"LC_ALL=$LC_ALL"}
        do      while   case  $c in     (*\'\''*) ;; (*) ! \
                                 set "" "${c%%=*}='\''${c#*=}$1'\'' $2" "$3"
                        esac;do  set    "'"'\''\${c##*\'}"'$@";  c=${c%\'\''*}
        done;   done;   LC_ALL=C a=$3 c=;set "" "$2 OPTARG='\''${#a}*("
        while   [ 0 -ne "${#a}" ]
        do      case $a in      ([[:print:][:cntrl:]]*)
                        case    $a in   (['"$(printf \\1-\\77)"']*)
                                        b=0;;   (*)     b=1
                        esac;;  (['"$(  printf  \\200-\\277)"']*)
                                        b=2;;   (*)     b=3
                esac;    set    '"$(ctbl)"'     "$@"
                eval "   set    \"\${$((b+1))%"'\''"${a%"${a#?}"}"*}" "$6"'\''
                a=${a#?};set    "$((b=b*100+${#1}+${#1}/8*2)))" \
                                "$2(o$((c+=1))=$b)>=(d$c=$((0$b)))|"
        done;   eval "   unset   LC_ALL  a b c;${2%?})'\''"
        return  "$((${OPTARG%%\**}-1))"
}'

Первый ctbl()- наверху - запускается только один раз. Он генерирует следующий вывод (который был отфильтрован sed -n lдля удобства печати) :

ctbl | sed -n l

 "\200\001\002\003\004\005\006\a\b\t$
\v\f\r\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\
\035\036\037 !\\"#$%&'()*+,-./0123456789:;<=>?" "@ABCDEFGHIJKLMNOPQRS\
TUVWXYZ[\\]^_\\`abcdefghijklmnopqrstuvwxyz{|}~\177" "\200\201\202\203\
\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\
\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\
\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\
\267\270\271\272\273\274\275\276\277" "\300\301\302\303\304\305\306\
\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\
\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\
\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\
\372\373\374\375\376\377"$

... которые представляют собой 8-битные байты (меньше NUL) , разделенные на четыре строки в кавычках, разделенных равномерно по границам в 64 байта. Могут быть представлены строки с восьмеричной диапазоны нравится \200\1-\77, \100-\177, \200-\277, \300-\377, где байты 128 используются в качестве места для держателя NUL.

Первая ctbl()цель существования состоит в том, чтобы генерировать эти строки так, чтобы они evalмогли определять вторую ctbl()функцию, буквально встраивая их после этого. Таким образом, на них можно ссылаться в функции без необходимости генерировать их снова каждый раз, когда они необходимы. Когда evalопределяет вторую ctbl()функцию, первая перестает быть.

Верхняя половина второй ctbl()функции здесь в основном вспомогательная - она ​​предназначена для переносимой и безопасной сериализации любого текущего состояния оболочки, на которое она может повлиять при вызове. Верхний цикл будет заключать в кавычки любые значения переменных, которые он может захотеть использовать, а затем складывать все результаты в свои позиционные параметры.

Первые две строки, тем не менее, сначала сразу возвращают 0 и устанавливают $OPTARGна то же самое, если первый аргумент функции не содержит хотя бы одного символа. И если это так, вторая строка сразу усекает свой первый аргумент только до первого символа - потому что функция обрабатывает только символ за раз. Важно, что он делает это в текущем контексте локали, что означает, что если символ может содержать более одного байта, то, если оболочка правильно поддерживает многобайтовые символы, она не будет отбрасывать никакие байты, кроме тех, которые отсутствуют в первый символ его первого аргумента.

        ${1:+":"}       return "$((OPTARG=0))"
        set     "" ""   "${1%"${1#?}"}"

Затем он выполняет цикл сохранения, если это вообще необходимо, и после этого переопределяет текущий контекст локали в локаль C для каждой категории, присваивая LC_ALLпеременную. С этого момента символ может состоять только из одного байта, и поэтому, если в первом символе его первого аргумента было несколько байтов, теперь каждый из них должен быть адресуемым как отдельный символ самостоятельно.

        LC_ALL=C

Именно по этой причине вторая половина функции является while циклом , а не последовательностью с одиночным прогоном. В большинстве случаев он, вероятно, будет выполняться только один раз за вызов, но, если оболочка, в которой ctbl()он определен правильно, обрабатывает многобайтовые символы, он может зацикливаться.

        while   [ 0 -ne "${#a}" ]
        do      case $a in      ([[:print:][:cntrl:]]*)
                        case    $a in   (['"$(printf \\1-\\77)"']*)
                                        b=0;;   (*)     b=1
                        esac;;  (['"$(  printf  \\200-\\277)"']*)
                                        b=2;;   (*)     b=3
                esac;    set    '"$(ctbl)"'     "$@"

Обратите внимание, что приведенная выше $(ctbl)подстановка команд оценивается только один раз - по evalпервоначальному определению функции - и что навсегда после того, как этот токен заменяется литеральным выводом этой подстановки команд, сохраненным в памяти оболочки. То же самое верно для двух caseподстановок команд шаблона. Эта функция никогда не вызывает subshell или любую другую команду. Он также никогда не будет пытаться читать или записывать ввод / вывод (за исключением случая с некоторым диагностическим сообщением оболочки - что, вероятно, указывает на ошибку) .

Также обратите внимание, что тест на непрерывность цикла не просто [ -n "$a" ], потому что, как я обнаружил, к сожалению, по какой-то причине bashоболочка делает:

char=$(printf \\1)
[ -n "$char" ] || echo but it\'s not null\!

but it's not null!

... и поэтому я явно сравниваю $alen с 0 для каждой итерации, что также необъяснимо ведет себя по-разному (читай: правильно) .

В caseпроверяет первые байты для включения в любом из наших четырех строк и сохраняет ссылку на набор байте в $b. После этого первые четыре позиционных параметра оболочки относятся setк строкам, встроенным evalи написанным ctbl()предшественником.

Затем, все, что осталось от первого аргумента, снова временно усекается до его первого символа - который теперь должен быть гарантированно одним байтом. Этот первый байт используется в качестве ссылки для удаления из хвоста строки, с которой он совпал, и ссылка в $bявляется eval'd для представления позиционного параметра, так что все, от байта ссылки до последнего байта в строке, может быть заменено. Три другие строки полностью исключаются из позиционных параметров.

               eval "   set    \"\${$((b+1))%"'\''"${a%"${a#?}"}"*}" "$6"'\''
               a=${a#?};set    "$((b=b*100+${#1}+${#1}/8*2)))" \
                                "$2(o$((c+=1))=$b)>=(d$c=$((0$b)))|"

На данный момент значение байта (по модулю 64) может быть указано как длина строки:

str=$(printf '\200\1\2\3\4\5\6\7')
ref=$(printf \\4)
str=${str%"$ref"*}
echo "${#str}"

4

Затем делается небольшая математика для согласования модуля, основанного на значении in $b, первый байт in $aпостоянно удаляется, и вывод для текущего цикла добавляется в стек в ожидании завершения, прежде чем цикл перезагружается, чтобы проверить, $aдействительно ли он пуст.

    eval "   unset   LC_ALL  a b c;${2%?})'\''"
    return  "$((${OPTARG%%\**}-1))"

Когда $aопределенно пусто, все имена и состояния - за исключением $OPTARG- что функция, на которую влияют в течение всего ее выполнения, восстанавливаются до своего предыдущего состояния - независимо от того, установлено ли оно, а не равно ли оно, установлено и равно нулю или не установлено - и выходные данные сохранены чтобы $OPTARGкак функция возвращается. Фактическое возвращаемое значение на единицу меньше, чем общее число байтов в первом символе его первого аргумента - поэтому любой однобайтовый символ возвращает ноль, а любой многобайтовый символ вернет больше нуля - и его формат вывода немного странный.

Значение ctbl()экономит $OPTARGявляется допустимыми оболочками арифметического выражения , которое, если оценено, будет одновременно установить имена переменных форм $o1, $d1, $o2, $d2в десятичное и восьмеричных значения всех соответствующих байт в первом символе первого аргумента, но в конечном счете оценить в общую сложности количество байтов в первом аргументе. Когда я писал это, у меня был особый вид рабочего процесса, и я думаю, что демонстрация в порядке.

Я часто нахожу причину, чтобы разобрать строку getoptsкак:

str=some\ string OPTIND=1
while   getopts : na  -"$str"
do      printf %s\\n "$OPTARG"
done

s
o
m
e

s
t
r
i
n
g

Я, вероятно, делаю немного больше, чем просто печатаю его символ в строке, но все возможно. В любом случае, я еще не нашел , getoptsчто будет правильно делать (удар, - dash«S getoptsделает его обугливается на гольца, но , bashбезусловно , не делает) :

str=ŐőŒœŔŕŖŗŘřŚśŜŝŞş  OPTIND=1
while   getopts : na  -"$str"
do      printf %s\\n "$OPTARG"
done|   od -tc

0000000 305  \n 220  \n 305  \n 221  \n 305  \n 222  \n 305  \n 223  \n
0000020 305  \n 224  \n 305  \n 225  \n 305  \n 226  \n 305  \n 227  \n
0000040 305  \n 230  \n 305  \n 231  \n 305  \n 232  \n 305  \n 233  \n
0000060 305  \n 234  \n 305  \n 235  \n 305  \n 236  \n 305  \n 237  \n
0000100

Хорошо. Итак, я попытался ...

str=ŐőŒœŔŕŖŗŘřŚśŜŝŞş
while   [ 0 -ne "${#str}" ]
do      printf %c\\n "$str"    #identical results for %.1s
        str=${str#?}
done|   od -tc

#dash
0000000 305  \n 220  \n 305  \n 221  \n 305  \n 222  \n 305  \n 223  \n
0000020 305  \n 224  \n 305  \n 225  \n 305  \n 226  \n 305  \n 227  \n
0000040 305  \n 230  \n 305  \n 231  \n 305  \n 232  \n 305  \n 233  \n
0000060 305  \n 234  \n 305  \n 235  \n 305  \n 236  \n 305  \n 237  \n
0000100

#bash
0000000 305  \n 305  \n 305  \n 305  \n 305  \n 305  \n 305  \n 305  \n
*
0000040

Этот тип рабочего процесса - байт для байта / символа для типа символа - это тот, который я часто использую при выполнении tty. На переднем крае ввода вам нужно знать значения символов, как только вы их читаете, и вам нужны их размеры (особенно при подсчете столбцов) , и вам нужно, чтобы символы были целыми символами.

И вот теперь у меня есть ctbl():

str=ŐőŒœŔŕŖŗŘřŚśŜŝŞş
while [ 0 -ne "${#str}" ]
do    ctbl "$str"
      printf "%.$(($OPTARG))s\t::\t$OPTARG\t::\t$?\t::\t\\$o1\\$o2\n" "$str"
      str=${str#?}
done

Ő   ::  2*((o1=305)>=(d1=197)|(o2=220)>=(d2=144))   ::  1   ::  Ő
ő   ::  2*((o1=305)>=(d1=197)|(o2=221)>=(d2=145))   ::  1   ::  ő
Œ   ::  2*((o1=305)>=(d1=197)|(o2=222)>=(d2=146))   ::  1   ::  Œ
œ   ::  2*((o1=305)>=(d1=197)|(o2=223)>=(d2=147))   ::  1   ::  œ
Ŕ   ::  2*((o1=305)>=(d1=197)|(o2=224)>=(d2=148))   ::  1   ::  Ŕ
ŕ   ::  2*((o1=305)>=(d1=197)|(o2=225)>=(d2=149))   ::  1   ::  ŕ
Ŗ   ::  2*((o1=305)>=(d1=197)|(o2=226)>=(d2=150))   ::  1   ::  Ŗ
ŗ   ::  2*((o1=305)>=(d1=197)|(o2=227)>=(d2=151))   ::  1   ::  ŗ
Ř   ::  2*((o1=305)>=(d1=197)|(o2=230)>=(d2=152))   ::  1   ::  Ř
ř   ::  2*((o1=305)>=(d1=197)|(o2=231)>=(d2=153))   ::  1   ::  ř
Ś   ::  2*((o1=305)>=(d1=197)|(o2=232)>=(d2=154))   ::  1   ::  Ś
ś   ::  2*((o1=305)>=(d1=197)|(o2=233)>=(d2=155))   ::  1   ::  ś
Ŝ   ::  2*((o1=305)>=(d1=197)|(o2=234)>=(d2=156))   ::  1   ::  Ŝ
ŝ   ::  2*((o1=305)>=(d1=197)|(o2=235)>=(d2=157))   ::  1   ::  ŝ
Ş   ::  2*((o1=305)>=(d1=197)|(o2=236)>=(d2=158))   ::  1   ::  Ş
ş   ::  2*((o1=305)>=(d1=197)|(o2=237)>=(d2=159))   ::  1   ::  ş

Обратите внимание, что на ctbl()самом деле $[od][12...]переменные не определяются - они никогда не оказывают какого-либо длительного влияния на любое состояние, но $OPTARG- но только помещают строку, $OPTARGкоторая может быть использована для их определения, - так я получаю вторую копию каждого символа выше, выполняя, printf "\\$o1\\$o2"потому что они устанавливаются каждый раз, когда я оцениваю $(($OPTARG)). Но где я это делаю я также объявить длину поля модификатор printf«s %sформат строки аргумента, и потому , что выражение всегда вычисляется на общее число байтов в характере, я получаю весь характер на выходе , когда я делаю:

printf %.2s "$str"
mikeserv
источник
Вы должны принять участие в конкурсе запутанного кода Bash!
Здравствуйте, до свидания
1
@ Здравствуйте, это не код Bash . и при этом это не запутано. чтобы увидеть запутывание, пожалуйста, обратитесь к тому [ "$(printf \\1)" ]|| ! echo but its not null!же, не стесняйтесь лучше познакомиться с практикой значимых комментариев, если вы не порекомендуете действительно такой конкурс ...?
mikeserv
Нет, не то, что я написал, это просто еще один способ сказать, что ваш код очень запутан (по крайней мере, для меня), но, возможно, он не должен был быть легко понятным. Если это не bash, то какой это язык?
Привет, до свидания,
@HelloGoodbye - это shкомандный язык POSIX . bashявляется Борн снова supraset в том же самом, и в значительной степени крутого мотиватора для большей части ухода , обеспечиваемого выше по отношению к широко портативным, уверенным расширению и пространству имен почетно размерам символов любого рода. bashЯ должен был уже справиться с большей частью этого, но cязык printfбыл, и, возможно, имеет недостатки вышеупомянутых возможностей.
mikeserv
Я все еще склонен использовать printf "% d" "'$ char" для простоты и читабельности. Мне любопытно, какие проблемы это ставит меня перед адресами решения @ mikeserv? Есть ли что-то большее, чем некоторые управляющие символы, влияющие на код возврата (который, я считаю, был его точкой в ​​приведенном выше комментарии)?
Алекс Янсен
3

Не скрипт оболочки, но работает

awk 'BEGIN{for( i=97; i<=122;i++) printf "%c %d\n",i,i }'  

Образец вывода

xieerqi:$ awk 'BEGIN{for( i=97; i<=122;i++) printf "%c %d\n",i,i }' | head -n 5                                    
a 97
b 98
c 99
d 100
e 101
Сергей Колодяжный
источник
2
  • выберите символ, затем нажмите CTRL + C
  • открытый konsole
  • и введите: xxd<press enter>
  • затем нажмите <SHIFT+INSERT><CTRL+D>

вы получите что-то вроде:

mariank@dd903c5n1 ~ $ xxd
û0000000: fb 

Вы знаете, что символ, который вы вставили, имеет шестнадцатеричный код 0xfb

Мариан Клен
источник