Respuestas:
Defina estas dos funciones (generalmente disponibles en otros idiomas):
chr() {
[ "$1" -lt 256 ] || return 1
printf "\\$(printf '%03o' "$1")"
}
ord() {
LC_CTYPE=C printf '%d' "'$1"
}
Uso:
chr 65
A
ord A
65
printf "\\$(printf '%03o' "$1")"
, '%03o'
, LC_CTYPE=C
y la comilla simple en "'$1"
Do?
Puedes ver todo el conjunto con:
$ man ascii
Obtendrá tablas en octal, hexadecimal y decimal.
Si desea extenderlo a los caracteres UTF-8:
$ perl -CA -le 'print ord shift' 😈
128520
$ perl -CS -le 'print chr shift' 128520
😈
Con bash
, ksh
o zsh
las órdenes internas:
$ printf "\U$(printf %08x 128520)\n"
😈
iceweasel
en Debian sid
. La fuente confirmada por la consola web de iceweasel es "DejaVu Sans" y tengo instalados los paquetes ttf-dejavu ttf-dejavu-core ttf-dejavu-extra que provienen de Debian con upstream en dejavu-fonts.org
Esto funciona bien
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.
exactamente equivalente a:
echo -n "A" | od -An -tuC ### Not all shells honor the '-n'.
echo -n
suprime la nueva línea final eliminando la necesidad detr -d "\n"
echo
, no en ecos compatibles con Unix, por ejemplo. printf %s A
Sería el portátil.
Voy por la solución Bash simple (y elegante?):
for i in {a..z}; do echo $(printf "%s %d" "$i" "'$i"); done
En un script puedes usar lo siguiente:
CharValue="A"
AscValue=`printf "%d" "'$CharValue"
Observe la comilla simple antes de CharValue. Está obligado ...
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))"
}'
El primero ctbl()
, en la parte superior, solo se ejecuta una vez. Genera el siguiente resultado (que se ha filtrado a través sed -n l
de la capacidad de impresión) :
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"$
... que son todos bytes de 8 bits (menos NUL
) , divididos en cuatro cadenas entre comillas divididas de manera uniforme en los límites de 64 bytes. Las cuerdas pueden ser representados con rangos octales gusta \200\1-\77
, \100-\177
, \200-\277
, \300-\377
, donde byte 128 se utiliza como un lugar-soporte para NUL
.
El primer ctbl()
propósito de la existencia del primero es generar esas cadenas para que eval
puedan definir la segunda ctbl()
función con ellas literalmente incrustadas a partir de entonces. De ese modo, se puede hacer referencia a ellos en la función sin necesidad de generarlos nuevamente cada vez que se necesiten. Cuando eval
define la segunda ctbl()
función, la primera dejará de ser.
La mitad superior de la segunda ctbl()
función es principalmente auxiliar aquí: está diseñada para serializar de forma portátil y segura cualquier estado de shell actual que pueda afectar cuando se llama. El bucle superior citará las comillas en los valores de cualquier variable que quiera usar, y luego apilará todos los resultados en sus parámetros posicionales.
Sin embargo, las dos primeras líneas devuelven inmediatamente 0 y se establecen $OPTARG
en el mismo si el primer argumento de la función no contiene al menos un carácter. Y si lo hace, la segunda línea trunca inmediatamente su primer argumento solo a su primer carácter, porque la función solo maneja un carácter a la vez. Es importante destacar que hace esto en el contexto local actual, lo que significa que si un carácter puede comprender más de un byte, entonces, siempre que el shell admita caracteres de varios bytes, no descartará ningún byte excepto aquellos que no están en el primer personaje de su primer argumento.
${1:+":"} return "$((OPTARG=0))"
set "" "" "${1%"${1#?}"}"
Luego realiza el ciclo de guardar si es necesario, y luego redefine el contexto de la configuración regional actual a la configuración regional C para cada categoría mediante la asignación a la LC_ALL
variable. A partir de este momento, un carácter solo puede consistir en un solo byte, por lo que si hubiera varios bytes en el primer carácter de su primer argumento, ahora estos deberían ser direccionables como caracteres individuales por derecho propio.
LC_ALL=C
Es por esta razón que la segunda mitad de la función es un while
bucle , a diferencia de una secuencia de ejecución individual. En la mayoría de los casos, probablemente se ejecutará solo una vez por llamada, pero, si el shell en el que ctbl()
se define correctamente maneja los caracteres de varios bytes, podría repetirse.
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)"' "$@"
Tenga en cuenta que la anterior $(ctbl)
sustitución comando sólo se evalúa una vez cada vez - por eval
cuando la función se define inicialmente - y que siempre después de que el distintivo se sustituye por la salida literal de que la sustitución de comandos como guarda en la memoria de la cáscara. Lo mismo es cierto para las dos case
sustituciones de comando de patrón. Esta función nunca llama a un subshell ni a ningún otro comando. Tampoco intentará leer o escribir entradas / salidas (excepto en el caso de algún mensaje de diagnóstico de shell, que probablemente indica un error) .
También tenga en cuenta que la prueba de continuidad del bucle no es simplemente [ -n "$a" ]
, porque, como descubrí para mi frustración, por alguna razón un bash
shell lo hace:
char=$(printf \\1)
[ -n "$char" ] || echo but it\'s not null\!
but it's not null!
... y entonces comparo explícitamente $a
len a 0 para cada iteración, que, también inexplicablemente, se comporta de manera diferente (léase: correctamente) .
Los case
cheques el primer byte para su inclusión en cualquiera de nuestros cuatro cuerdas y almacena una referencia al conjunto del byte $b
. Luego, los primeros cuatro parámetros posicionales del shell son set
las cadenas incrustadas eval
y escritas por ctbl()
el predecesor.
A continuación, lo que queda del primer argumento se vuelve a truncar temporalmente a su primer carácter, que ahora debe asegurarse de que sea un solo byte. Este primer byte se utiliza como una referencia a la tira de la cola de la cadena de la que corresponde y la referencia en $b
está eval
'd para representar un parámetro de posición de modo de todo, desde el byte de referencia al último byte en la cadena puede ser sustituido de distancia. Las otras tres cadenas se eliminan completamente de los parámetros posicionales.
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)))|"
En este punto, el valor del byte (módulo 64) puede referenciarse como el len de la cadena:
str=$(printf '\200\1\2\3\4\5\6\7')
ref=$(printf \\4)
str=${str%"$ref"*}
echo "${#str}"
4
Luego se hace un poco de matemática para conciliar el módulo en función del valor $b
, el primer byte $a
se elimina permanentemente y la salida para el ciclo actual se agrega a una pila hasta que se complete antes de que el ciclo se recicle para verificar si $a
está realmente vacío.
eval " unset LC_ALL a b c;${2%?})'\''"
return "$((${OPTARG%%\**}-1))"
Cuando $a
definitivamente está vacío, todos los nombres y estados, con la excepción de $OPTARG
que la función afectada a lo largo de su ejecución se restaura a su estado anterior, ya sea establecido y no nulo, establecido y nulo o sin establecer, y la salida se guarda a $OPTARG
como vuelve la función. El valor de retorno real es uno menos que el número total de bytes en el primer carácter de su primer argumento, por lo que cualquier carácter de un solo byte devuelve cero y cualquier carácter de varios bytes devolverá más de cero, y su formato de salida es un poco extraño.
El valor ctbl()
se guarda en $OPTARG
una cáscara de expresión aritmética válido que, si se evalúa, fijará al mismo tiempo los nombres de variables de las formas $o1
, $d1
, $o2
, $d2
al decimal y los valores octales de todos los bytes respectivos en el primer carácter de su primer argumento, pero en última instancia evaluar al total número de bytes en su primer argumento. Tenía un tipo específico de flujo de trabajo en mente al escribir esto, y creo que tal vez sea necesario hacer una demostración.
A menudo encuentro una razón para separar una cadena con getopts
like:
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
Probablemente haga un poco más que imprimirlo en un carácter por línea, pero todo es posible. En cualquier caso, todavía no he encontrado una getopts
que va a hacer correctamente (huelga que - dash
Es getopts
lo hace charla por char, pero bash
definitivamente no lo hace) :
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
Okay. Entonces intenté ...
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
Ese tipo de flujo de trabajo, el byte para byte / char para char kind, es uno en el que a menudo me meto cuando hago cosas de tty. En la vanguardia de la entrada, debe conocer los valores de caracteres tan pronto como los lea, y necesita sus tamaños (especialmente al contar columnas) , y necesita que los caracteres sean caracteres enteros .
Y ahora tengo 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 :: ş
Tenga en cuenta que en ctbl()
realidad no define las $[od][12...]
variables, nunca tiene un efecto duradero en ningún estado $OPTARG
, pero solo coloca la cadena $OPTARG
que se puede usar para definirlas, que es la forma en que obtengo la segunda copia de cada carácter anterior printf "\\$o1\\$o2"
porque se establecen cada vez que evalúo $(($OPTARG))
. Pero cuando lo hago también estoy declarando un modificador de la longitud del campo de printf
's %s
formato argumento de cadena, y debido a la expresión da siempre como resultado el número total de bytes de un personaje, me sale todo el carácter en la salida cuando lo haga:
printf %.2s "$str"
[ "$(printf \\1)" ]|| ! echo but its not null!
mientras tanto, siéntase libre de familiarizarse mejor con la práctica de comentarios significativos, a menos que recomiende un concurso real de este tipo ...?
sh
lenguaje de comando POSIX . bash
es un bourne nuevamente superior de lo mismo, y en gran parte un motivador precipitado para gran parte de la atención brindada anteriormente hacia tamaños de caracteres honorables de gran tamaño, portátiles y autoexpandibles de cualquier tipo. bash
ya debería manejar gran parte de esto, pero el c
lenguaje printf
era, y tal vez es, deficiente la capacidad proporcionada anteriormente.
No es un script de shell, pero funciona
awk 'BEGIN{for( i=97; i<=122;i++) printf "%c %d\n",i,i }'
Salida de muestra
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>
obtienes algo como:
mariank@dd903c5n1 ~ $ xxd
û0000000: fb
sabes que el símbolo que pegaste tiene código hexadecimal 0xfb
"'A"
es correcta, mientras que si se utiliza"A"
dirá:A: invalid number
. Parece que está hecho en el lado printf (es decir, en el shell, de"'A"
hecho hay 2 caracteres, ay'
aA
. Esos se pasan a printf. Y en el contexto printf, se convierte al valor ascii de A, (y finalmente se imprime como decimal gracias a'%d'
. Úselo'Ox%x'
para mostrarlo en hexa o'0%o'
para tenerlo en octal))