Ответы:
Определите эти две функции (обычно доступные на других языках):
chr() {
[ "$1" -lt 256 ] || return 1
printf "\\$(printf '%03o' "$1")"
}
ord() {
LC_CTYPE=C printf '%d' "'$1"
}
Использование:
chr 65
A
ord A
65
printf "\\$(printf '%03o' "$1")"
, '%03o'
, LC_CTYPE=C
и апостроф в "'$1"
делах?
Вы можете увидеть весь набор с:
$ man ascii
Вы получите таблицы в восьмеричном, шестнадцатеричном и десятичном виде.
Если вы хотите расширить его до символов 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"
😈
Это хорошо работает,
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'.
echo -n
подавляет завершающий tr -d "\n"
echo
, не в Unix-совместимом эхо. printf %s A
будет портативным.
Я собираюсь для простого (и элегантного?) Решения Bash:
for i in {a..z}; do echo $(printf "%s %d" "$i" "'$i"); done
В сценарии вы можете использовать следующее:
CharValue="A"
AscValue=`printf "%d" "'$CharValue"
Обратите внимание на одинарную кавычку перед CharValue. Обязан ...
printf "%d"
.
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!
... и поэтому я явно сравниваю $a
len с 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"
[ "$(printf \\1)" ]|| ! echo but its not null!
же, не стесняйтесь лучше познакомиться с практикой значимых комментариев, если вы не порекомендуете действительно такой конкурс ...?
sh
командный язык POSIX . bash
является Борн снова supraset в том же самом, и в значительной степени крутого мотиватора для большей части ухода , обеспечиваемого выше по отношению к широко портативным, уверенным расширению и пространству имен почетно размерам символов любого рода. bash
Я должен был уже справиться с большей частью этого, но c
язык printf
был, и, возможно, имеет недостатки вышеупомянутых возможностей.
Не скрипт оболочки, но работает
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
konsole
xxd<press enter>
<SHIFT+INSERT><CTRL+D>
вы получите что-то вроде:
mariank@dd903c5n1 ~ $ xxd
û0000000: fb
Вы знаете, что символ, который вы вставили, имеет шестнадцатеричный код 0xfb
"'A"
правильно тогда , если вы используете"A"
это будет сказать:A: invalid number
. Кажется, это сделано на стороне printf (то есть в оболочке"'A"
действительно 2 символа, a'
и aA
. Они передаются в printf. А в контексте printf он преобразуется в значение ascii A, (и, наконец, печатается как десятичное число благодаря'%d'
. Используйте,'Ox%x'
чтобы показать его в гекса или'0%o'
чтобы иметь его в восьмеричном))