Jawaban:
Tentukan dua fungsi ini (biasanya tersedia dalam bahasa lain):
chr() {
[ "$1" -lt 256 ] || return 1
printf "\\$(printf '%03o' "$1")"
}
ord() {
LC_CTYPE=C printf '%d' "'$1"
}
Pemakaian:
chr 65
A
ord A
65
printf "\\$(printf '%03o' "$1")"
, '%03o'
, LC_CTYPE=C
dan kutipan tunggal di "'$1"
do?
Anda dapat melihat seluruh rangkaian dengan:
$ man ascii
Anda akan mendapatkan tabel dalam oktal, hex, dan desimal.
Jika Anda ingin memperluasnya ke karakter UTF-8:
$ perl -CA -le 'print ord shift' 😈
128520
$ perl -CS -le 'print chr shift' 128520
😈
Dengan bash
, ksh
atau zsh
bawaan:
$ printf "\U$(printf %08x 128520)\n"
😈
iceweasel
aktif Debian sid
. Font yang dikonfirmasi oleh konsol web iceweasel adalah "DejaVu Sans" dan saya punya ttf-dejavu ttf-dejavu-core ttf-dejavu-paket tambahan yang diinstal yang berasal dari Debian dengan upstream di dejavu-fonts.org
Ini bekerja dengan baik,
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.
persis sama dengan:
echo -n "A" | od -An -tuC ### Not all shells honor the '-n'.
echo -n
menekan trailing newline menghilangkan kebutuhan untuktr -d "\n"
echo
, tidak dalam gema yang sesuai dengan Unix misalnya. printf %s A
akan menjadi yang portabel.
Saya akan mencari solusi Bash yang sederhana (dan elegan?):
for i in {a..z}; do echo $(printf "%s %d" "$i" "'$i"); done
Untuk dalam skrip Anda dapat menggunakan yang berikut:
CharValue="A"
AscValue=`printf "%d" "'$CharValue"
Perhatikan kutipan tunggal sebelum CharValue. Itu wajib ...
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))"
}'
Yang pertama ctbl()
- di atas sana - hanya berjalan satu kali. Ini menghasilkan output berikut (yang telah difilter sed -n l
demi printability) :
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"$
... yang semuanya 8-bit byte (kurang NUL
) , dibagi menjadi empat string yang dikutip shell dibagi secara merata pada batas 64-byte. String mungkin diwakili dengan rentang oktal suka \200\1-\77
, \100-\177
, \200-\277
, \300-\377
, di mana byte 128 digunakan sebagai tempat pemegang untuk NUL
.
Pertama ctbl()
's seluruh tujuan untuk keberadaan adalah untuk menghasilkan string tersebut sehingga eval
dapat menentukan kedua ctbl()
fungsi dengan mereka secara harfiah tertanam setelahnya. Dengan cara itu mereka dapat direferensikan dalam fungsi tanpa perlu membuatnya lagi setiap kali dibutuhkan. Ketika eval
mendefinisikan ctbl()
fungsi kedua , yang pertama akan berhenti.
Setengah bagian atas dari ctbl()
fungsi kedua sebagian besar tambahan di sini - ia dirancang untuk membuat serialisasi portabilitas shell dengan aman saat ini dan apa pun yang mungkin mempengaruhi ketika dipanggil. Loop atas akan mengutip setiap kutipan dalam nilai-nilai variabel apa pun yang mungkin ingin digunakan, dan kemudian menumpuk semua hasil dalam parameter posisinya.
Dua baris pertama, bagaimanapun, pertama segera mengembalikan 0 dan diatur $OPTARG
ke sama jika argumen pertama fungsi tidak mengandung setidaknya satu karakter. Dan jika ya, baris kedua segera memotong argumen pertamanya hanya karakter pertama - karena fungsi hanya menangani karakter pada suatu waktu. Yang penting, ia melakukan ini dalam konteks lokal saat ini, yang berarti bahwa jika karakter dapat terdiri lebih dari satu byte, maka, asalkan shell dengan benar mendukung multi-byte chars, ia tidak akan membuang byte kecuali kecuali yang tidak ada dalam karakter pertama dari argumen pertamanya.
${1:+":"} return "$((OPTARG=0))"
set "" "" "${1%"${1#?}"}"
Itu kemudian melakukan loop simpan jika diperlukan, dan setelah itu mendefinisikan kembali konteks lokal saat ini ke lokal C untuk setiap kategori dengan menetapkan LC_ALL
variabel. Dari titik ini, satu karakter hanya dapat terdiri dari satu byte, dan jadi jika ada beberapa byte dalam karakter pertama dari argumen pertama, ini sekarang harus dialamatkan masing-masing sebagai karakter individu dalam hak mereka sendiri.
LC_ALL=C
Karena alasan inilah maka bagian kedua dari fungsi adalah while
loop , sebagai lawan dari urutan berjalan tunggal. Dalam kebanyakan kasus, ia mungkin hanya akan mengeksekusi satu kali per panggilan, tetapi, jika shell yang ctbl()
didefinisikan dengan benar menangani karakter multi-byte, itu mungkin loop.
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)"' "$@"
Perhatikan bahwa $(ctbl)
substitusi perintah di atas hanya pernah dievaluasi sekali - pada eval
saat fungsi awalnya didefinisikan - dan bahwa selamanya setelah itu token diganti dengan output literal dari substitusi perintah yang disimpan ke dalam memori shell. Hal yang sama berlaku untuk dua case
pergantian perintah pola. Fungsi ini tidak pernah memanggil subkulit atau perintah lainnya. Ini juga tidak akan pernah mencoba untuk membaca atau menulis input / output (kecuali dalam kasus beberapa pesan diagnostik shell - yang mungkin menunjukkan bug) .
Perhatikan juga bahwa tes untuk kontinuitas loop tidak hanya [ -n "$a" ]
, karena, seperti yang saya temukan pada frustasi saya, untuk beberapa alasan bash
shell tidak:
char=$(printf \\1)
[ -n "$char" ] || echo but it\'s not null\!
but it's not null!
... dan jadi saya secara eksplisit membandingkan $a
len ke 0 untuk setiap iterasi, yang, juga tidak dapat dijelaskan, berperilaku berbeda (baca: dengan benar) .
The case
cek byte pertama untuk dimasukkan dalam salah satu dari empat senar dan toko referensi ke set byte dalam $b
. Setelah itu, empat parameter posisi pertama shell adalah set
string yang disematkan eval
dan ditulis oleh ctbl()
pendahulunya.
Selanjutnya, apa pun yang tersisa dari argumen pertama sekali lagi terpotong sementara untuk karakter pertamanya - yang sekarang harus dipastikan sebagai byte tunggal. Byte pertama ini digunakan sebagai referensi untuk melepas dari ujung string yang cocok dan referensi di $b
adalah eval
untuk mewakili parameter posisi sehingga segala sesuatu dari byte referensi ke byte terakhir dalam string dapat diganti. Tiga string lainnya dijatuhkan sepenuhnya dari parameter posisi.
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)))|"
Pada titik ini nilai byte (modulo 64) dapat dirujuk sebagai len string:
str=$(printf '\200\1\2\3\4\5\6\7')
ref=$(printf \\4)
str=${str%"$ref"*}
echo "${#str}"
4
Sedikit matematika kemudian dilakukan untuk mendamaikan modulus berdasarkan nilai dalam $b
, byte pertama in $a
secara permanen dihapus, dan output untuk siklus saat ini ditambahkan ke tumpukan sambil menunggu penyelesaian sebelum loop daur ulang untuk memeriksa apakah $a
benar-benar kosong.
eval " unset LC_ALL a b c;${2%?})'\''"
return "$((${OPTARG%%\**}-1))"
Ketika $a
benar-benar kosong, semua nama dan negara - dengan pengecualian $OPTARG
- bahwa fungsi yang terpengaruh selama pelaksanaannya dikembalikan ke keadaan sebelumnya - apakah disetel dan bukan nol, setel dan batal, atau tidak disetel - dan hasilnya disimpan untuk $OPTARG
sebagai fungsi kembali. Nilai pengembalian aktual adalah satu kurang dari jumlah total byte dalam karakter pertama dari argumen pertamanya - sehingga setiap karakter byte tunggal mengembalikan nol dan karakter multi-byte akan mengembalikan lebih dari nol - dan format outputnya sedikit aneh.
Nilai ctbl()
menghemat untuk $OPTARG
adalah shell ekspresi aritmatika valid yang, jika dievaluasi, bersamaan akan mengatur nama variabel bentuk $o1
, $d1
, $o2
, $d2
untuk desimal dan nilai-nilai oktal dari semua byte masing-masing dalam karakter pertama dari argumen pertama, tapi akhirnya mengevaluasi total jumlah byte dalam argumen pertamanya. Saya memiliki jenis alur kerja tertentu dalam pikiran ketika menulis ini, dan saya pikir mungkin demonstrasi dalam rangka.
Saya sering menemukan alasan untuk memisahkan string dengan getopts
seperti:
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
Saya mungkin melakukan sedikit lebih dari sekedar mencetaknya char per baris, tapi semuanya mungkin. Dalam hal apapun, saya belum menemukan getopts
yang benar akan melakukan (strike itu - dash
's getopts
melakukannya char by char, tapi bash
pasti tidak) :
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
Baik. Jadi saya mencoba ...
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
Alur kerja semacam itu - byte untuk byte / char untuk jenis char - adalah salah satu yang sering saya bahas ketika melakukan hal-hal tty. Di ujung depan input, Anda perlu mengetahui nilai char segera setelah Anda membacanya, dan Anda perlu ukurannya (terutama ketika menghitung kolom) , dan Anda perlu karakter untuk menjadi karakter keseluruhan .
Dan sekarang saya punya 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 :: ş
Catatan yang ctbl()
tidak benar-benar mendefinisikan $[od][12...]
variabel - tidak pernah memiliki efek abadi pada keadaan apa pun tetapi $OPTARG
- tetapi hanya menempatkan string $OPTARG
yang dapat digunakan untuk mendefinisikan mereka - yang adalah bagaimana saya mendapatkan salinan kedua dari masing-masing karakter di atas dengan melakukan printf "\\$o1\\$o2"
karena mereka diset setiap kali saya mengevaluasi $(($OPTARG))
. Tapi di mana saya melakukannya saya juga mendeklarasikan panjang lapangan pengubah untuk printf
's %s
Format argumen string, dan karena ekspresi selalu mengevaluasi dengan jumlah total byte dalam karakter, saya mendapatkan seluruh karakter pada output ketika saya lakukan:
printf %.2s "$str"
[ "$(printf \\1)" ]|| ! echo but its not null!
sementara itu, merasa bebas untuk lebih mengenal diri sendiri dengan praktik komentar yang bermakna, kecuali jika Anda merekomendasikan kontes yang sebenarnya ...?
sh
bahasa perintah POSIX . bash
adalah bourne lagi supraset yang sama, dan sebagian besar motivator yang terjal untuk banyak perawatan yang diberikan di atas menuju ukuran karakter yang luas portabel, memperluas diri dan namespace jenis apa pun. bash
harus menangani sebagian besar dari ini, tetapi c
bahasa printf
itu, dan mungkin, kekurangan kemampuan di atas disediakan.
Bukan skrip shell, tetapi berfungsi
awk 'BEGIN{for( i=97; i<=122;i++) printf "%c %d\n",i,i }'
Output sampel
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>
Anda mendapatkan sesuatu seperti:
mariank@dd903c5n1 ~ $ xxd
û0000000: fb
Anda tahu simbol yang Anda tempel memiliki kode hex 0xfb
"'A"
benar sedangkan jika Anda menggunakan"A"
ia akan berkata:A: invalid number
. Tampaknya itu dilakukan pada sisi printf (yaitu, di shell,"'A"
memang 2 karakter, a'
dan aA
. Mereka dilewatkan ke printf. Dan dalam konteks printf, itu dikonversi ke nilai ascii dari A, (dan akhirnya dicetak sebagai desimal berkat'%d'
. Gunakan'Ox%x'
untuk menunjukkannya dalam heksa atau'0%o'
memilikinya dalam oktal))