Odpowiedzi:
Zdefiniuj te dwie funkcje (zwykle dostępne w innych językach):
chr() {
[ "$1" -lt 256 ] || return 1
printf "\\$(printf '%03o' "$1")"
}
ord() {
LC_CTYPE=C printf '%d' "'$1"
}
Stosowanie:
chr 65
A
ord A
65
printf "\\$(printf '%03o' "$1")"
, '%03o'
, LC_CTYPE=C
a apostrof w "'$1"
zrobienia?
Możesz zobaczyć cały zestaw za pomocą:
$ man ascii
Otrzymasz tabele ósemkowe, szesnastkowe i dziesiętne.
Jeśli chcesz rozszerzyć go na znaki UTF-8:
$ perl -CA -le 'print ord shift' 😈
128520
$ perl -CS -le 'print chr shift' 128520
😈
Z bash
, ksh
lub zsh
builtins:
$ printf "\U$(printf %08x 128520)\n"
😈
iceweasel
on Debian sid
. Czcionką potwierdzoną przez konsolę internetową iceweasel jest „DejaVu Sans” i mam zainstalowane ttf-dejavu ttf-dejavu-core ttf-dejavu-extra, które pochodzą z Debiana z upstream na dejavu-fonts.org
To działa dobrze,
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.
dokładnie odpowiada:
echo -n "A" | od -An -tuC ### Not all shells honor the '-n'.
echo -n
tłumi końcowe znaki nowej linii, eliminując potrzebętr -d "\n"
echo
, nie na przykład w echach zgodnych z Uniksem. printf %s A
byłby przenośny.
Idę po proste (i eleganckie?) Rozwiązanie Bash:
for i in {a..z}; do echo $(printf "%s %d" "$i" "'$i"); done
W skrypcie możesz użyć następujących opcji:
CharValue="A"
AscValue=`printf "%d" "'$CharValue"
Zwróć uwagę na pojedynczy cytat przed CharValue. Jest to obowiązkowe ...
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))"
}'
Pierwszy ctbl()
- na górze - działa tylko raz. Generuje następujące dane wyjściowe (które zostały przefiltrowane sed -n l
ze względu na drukowalność) :
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"$
... które są 8-bitowymi bajtami (mniej NUL
) , podzielonymi na cztery ciągi cytowane w powłoce, podzielone równomiernie na granicach 64-bajtowych. Łańcuchy mogą być reprezentowane ósemkowe zakresach, takich jak \200\1-\77
, \100-\177
, \200-\277
, \300-\377
, gdzie bajt 128 jest wykorzystywane jako miejsce uchwytu dla NUL
.
Pierwszym ctbl()
celem istnienia jest wygenerowanie tych ciągów, aby eval
można było zdefiniować drugą ctbl()
funkcję z nimi dosłownie osadzonymi później. W ten sposób można do nich odwoływać się w funkcji bez potrzeby ich ponownego generowania za każdym razem, gdy są potrzebne. Kiedy eval
zdefiniuje drugą ctbl()
funkcję, pierwsza przestanie być.
Górna połowa drugiej ctbl()
funkcji jest tutaj głównie pomocnicza - jest zaprojektowana do przenośnego i bezpiecznego szeregowania dowolnego bieżącego stanu powłoki, na który może mieć wpływ, gdy zostanie wywołana. Górna pętla cytuje wszelkie cudzysłowy w wartościach dowolnych zmiennych, których może chcieć użyć, a następnie układa wszystkie wyniki w swoich parametrach pozycyjnych.
Pierwsze dwa wiersze jednak najpierw zwracają 0 i ustawiają $OPTARG
to samo, jeśli pierwszy argument funkcji nie zawiera co najmniej jednego znaku. A jeśli tak, druga linia natychmiast obcina pierwszy argument tylko do pierwszego znaku - ponieważ funkcja obsługuje tylko znak naraz. Co ważne, robi to w bieżącym kontekście ustawień regionalnych, co oznacza, że jeśli znak może zawierać więcej niż jeden bajt, to pod warunkiem, że powłoka poprawnie obsługuje znaki wielobajtowe, nie odrzuci żadnych bajtów oprócz tych, które nie znajdują się w pierwszy znak pierwszego argumentu.
${1:+":"} return "$((OPTARG=0))"
set "" "" "${1%"${1#?}"}"
Następnie wykonuje pętlę składowania, jeśli to konieczne, a następnie redefiniuje bieżący kontekst ustawień regionalnych na ustawienia regionalne C dla każdej kategorii, przypisując LC_ALL
zmienną. Od tego momentu znak może składać się tylko z jednego bajtu, więc jeśli pierwszy znak pierwszego argumentu zawierał wiele bajtów, każdy z nich powinien być teraz adresowalny jako osobny znak.
LC_ALL=C
Z tego powodu druga połowa funkcji jest while
pętlą , a nie pojedynczą sekwencją. W większości przypadków prawdopodobnie wykona się tylko raz na wywołanie, ale jeśli ctbl()
zdefiniowana powłoka poprawnie obsługuje znaki wielobajtowe, może zapętlić się.
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)"' "$@"
Zauważ, że powyższe $(ctbl)
podstawienie polecenia jest oceniane tylko raz - do eval
momentu, gdy funkcja jest wstępnie zdefiniowana - i że na zawsze po tym token zostanie zastąpiony dosłownym wyjściem tego podstawienia polecenia zapisanym w pamięci powłoki. To samo dotyczy dwóch case
podstawień poleceń wzorca. Ta funkcja nigdy nie wywołuje podpowłoki ani żadnego innego polecenia. Nigdy też nie będzie próbował czytać ani zapisywać danych wejściowych / wyjściowych (z wyjątkiem niektórych komunikatów diagnostycznych powłoki - co prawdopodobnie oznacza błąd) .
Zauważ też, że test ciągłości pętli nie jest po prostu [ -n "$a" ]
, ponieważ, jak zauważyłem, ku mojej frustracji, z jakiegoś powodu bash
powłoka:
char=$(printf \\1)
[ -n "$char" ] || echo but it\'s not null\!
but it's not null!
... i dlatego wyraźnie porównuję $a
len z wartością 0 dla każdej iteracji, która również w niewytłumaczalny sposób zachowuje się inaczej (czytaj: poprawnie) .
Te case
kontrole pierwszy bajt do umieszczenia w każdym z naszych czterech strun i zapisuje odwołanie do zestawu bajt w $b
. Następnie pierwsze cztery parametry pozycyjne powłoki odnoszą set
się do łańcuchów osadzonych eval
i zapisanych przez ctbl()
poprzednika.
Następnie wszystko, co pozostało z pierwszego argumentu, jest ponownie tymczasowo obcinane do jego pierwszego znaku - który powinien być teraz zapewniony jako jeden bajt. Ten pierwszy bajt jest używane jako odniesienie do taśmy z ogona ciągu którego dopasowane i odniesienia w $b
to eval
„d przedstawiają pozycyjną parametr tak począwszy od bajtu odniesieniu do ostatniego bajtu łańcucha mogą być podstawione dalej. Pozostałe trzy łańcuchy są całkowicie usuwane z parametrów pozycyjnych.
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)))|"
W tym momencie wartość bajtu (modulo 64) może być określana jako długość łańcucha:
str=$(printf '\200\1\2\3\4\5\6\7')
ref=$(printf \\4)
str=${str%"$ref"*}
echo "${#str}"
4
Następnie wykonuje się małą matematykę w celu uzgodnienia modułu na podstawie wartości in $b
, pierwszy bajt w $a
jest trwale usuwany, a dane wyjściowe dla bieżącego cyklu są dołączane do stosu w oczekiwaniu na zakończenie, zanim pętla zostanie ponownie przetworzona, aby sprawdzić, czy $a
rzeczywiście jest pusta.
eval " unset LC_ALL a b c;${2%?})'\''"
return "$((${OPTARG%%\**}-1))"
Gdy $a
zdecydowanie jest pusty, wszystkie nazwy i stan - z wyjątkiem $OPTARG
- że funkcja, której dotyczy problem w trakcie jej wykonywania, są przywracane do poprzedniego stanu - ustawionego i nie zerowego, ustawionego i zerowego lub nieuzbrojonego - a dane wyjściowe są zapisywane do $OPTARG
jak funkcja powraca. Rzeczywista wartość zwracana jest o jeden mniejsza niż całkowita liczba bajtów w pierwszym znaku pierwszego argumentu - więc dowolny znak jednobajtowy zwraca zero, a dowolny znak wielobajtowy zwróci więcej niż zero - a jego format wyjściowy jest nieco dziwny.
Wartość ctbl()
zapisuje się $OPTARG
to poprawne wyrażenie shell arytmetyka, że jeśli oceniane będą jednocześnie ustawić nazwy zmiennych form $o1
, $d1
, $o2
, $d2
na dziesiętne i ósemkowe wartości wszystkich odpowiednich bajtów pierwszego znaku swojego pierwszego argumentu, ale ostatecznie ocenić do całości liczba bajtów w pierwszym argumencie. Pisząc to, miałem na myśli szczególny rodzaj przepływu pracy i myślę, że może warto przeprowadzić demonstrację.
Często znajduję powód, aby rozdzielić ciąg znaków, na getopts
przykład:
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
Prawdopodobnie robię coś więcej niż tylko drukuję znak w wierszu, ale wszystko jest możliwe. W każdym razie, ja nie znaleźli jeszcze mają getopts
, że będzie prawidłowo zrobić (niepotrzebne, że - dash
jest getopts
to char przez char, ale bash
na pewno nie robi) :
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
Dobrze. Więc próbowałem ...
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
Tego rodzaju przepływ pracy - bajt dla bajtu / char dla rodzaju char - często wchodzę podczas robienia tty. Na początku wejścia musisz znać wartości znaków, gdy tylko je przeczytasz, i potrzebujesz ich rozmiarów (szczególnie przy liczeniu kolumn) i potrzebujesz znaków, aby były to całe znaki.
I tak teraz mam 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 :: ş
Zauważ, że ctbl()
tak naprawdę nie definiuje $[od][12...]
zmiennych - nigdy nie ma żadnego trwałego wpływu na żaden stan, ale $OPTARG
- ale tylko umieszcza ciąg znaków, $OPTARG
który można wykorzystać do ich zdefiniowania - w ten sposób otrzymuję drugą kopię każdego znaku powyżej, printf "\\$o1\\$o2"
ponieważ są ustawiane za każdym razem, gdy oceniam $(($OPTARG))
. Ale gdzie to zrobić ja też deklarowania długości pola modyfikator printf
„s %s
formatu argumentem ciąg, a ponieważ zawsze ocenia wyrażenie do całkowitej liczby bajtów w charakterze, mam całą postać na wyjściu kiedy zrobić:
printf %.2s "$str"
[ "$(printf \\1)" ]|| ! echo but its not null!
międzyczasie, nie krępuj się lepiej zapoznać ze znaczącą praktyką komentowania, chyba że polecasz taki konkurs ...?
sh
język poleceń POSIX . bash
jest znowu bourne tego samego, a w dużej mierze wyśmienity motywator do dużej staranności, jaką daje wyżej, w kierunku szeroko przenośnych, samorozwijających się i honorowych przestrzeni postaci o dowolnym rozmiarze. bash
powinien już poradzić sobie z większością tego problemu, ale c
język printf
był i być może ma niewystarczające możliwości powyżej podanych.
Nie skrypt powłoki, ale działa
awk 'BEGIN{for( i=97; i<=122;i++) printf "%c %d\n",i,i }'
Próbka wyjściowa
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>
dostajesz coś takiego:
mariank@dd903c5n1 ~ $ xxd
û0000000: fb
wiesz, że wklejony symbol ma kod szesnastkowy 0xfb
"'A"
jest prawidłowa, natomiast jeśli używasz"A"
powie:A: invalid number
. Wygląda na to, że zostało to zrobione po stronie printf (tzn. W powłoce"'A"
jest rzeczywiście 2 znaki, a'
i aA
. Są one przekazywane do printf. A w kontekście printf jest konwertowane na wartość ascii A, (i w końcu jest drukowane jako ułamek dziesiętny dzięki'%d'
. Użyj,'Ox%x'
aby pokazać go w heksie lub'0%o'
mieć go w formacie ósemkowym))