Respostas:
Defina estas duas funções (geralmente disponíveis em outros 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
ea única citação em "'$1"
tarefas?
Você pode ver o conjunto inteiro com:
$ man ascii
Você receberá tabelas em octal, hex e decimal.
Se você deseja estendê-lo para caracteres UTF-8:
$ perl -CA -le 'print ord shift' 😈
128520
$ perl -CS -le 'print chr shift' 128520
😈
Com bash
, ksh
ou zsh
builtins:
$ printf "\U$(printf %08x 128520)\n"
😈
iceweasel
on Debian sid
. A fonte confirmada pelo console da web do iceweasel é "DejaVu Sans" e eu tenho os pacotes ttf-dejavu ttf-dejavu-core ttf-dejavu-extra instalados que vêm do Debian com upstream em dejavu-fonts.org
Isso funciona bem,
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.
exatamente equivalente a:
echo -n "A" | od -An -tuC ### Not all shells honor the '-n'.
echo -n
suprime de arrasto nova linha eliminando a necessidade detr -d "\n"
echo
, não em ecos compatíveis com Unix, por exemplo. printf %s A
seria o portátil.
Estou optando pela solução Bash simples (e elegante?):
for i in {a..z}; do echo $(printf "%s %d" "$i" "'$i"); done
Para em um script você pode usar o seguinte:
CharValue="A"
AscValue=`printf "%d" "'$CharValue"
Observe a aspas simples antes do CharValue. É obrigatório ...
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))"
}'
O primeiro ctbl()
- no topo - apenas funciona uma vez. Ele gera a seguinte saída (que foi filtrada sed -n l
para fins de impressão) :
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 são todos os bytes de 8 bits (menos NUL
) , divididos em quatro cadeias de caracteres entre aspas, divididas igualmente nos limites de 64 bytes. As cordas podem ser representados com faixas octal gosto \200\1-\77
, \100-\177
, \200-\277
, \300-\377
, na qual o byte 128 é utilizada como um espaço reservado para NUL
.
O primeiro ctbl()
objetivo da existência é gerar essas strings para que eval
possam definir a segunda ctbl()
função com elas literalmente incorporadas posteriormente. Dessa forma, eles podem ser referenciados na função sem precisar gerá-los novamente cada vez que forem necessários. Quando eval
define a segunda ctbl()
função, a primeira deixará de existir.
A metade superior da segunda ctbl()
função é principalmente auxiliar aqui - ela foi projetada para serializar com segurança e portabilidade qualquer estado atual do shell que possa afetar quando for chamado. O loop superior citará quaisquer aspas nos valores de quaisquer variáveis que ele queira usar e, em seguida, empilhará todos os resultados em seus parâmetros posicionais.
As duas primeiras linhas, no entanto, retornam 0 imediatamente e são definidas $OPTARG
como iguais se o primeiro argumento da função não contiver pelo menos um caractere. E se isso acontecer, a segunda linha imediatamente trunca seu primeiro argumento apenas para seu primeiro caractere - porque a função lida apenas com um caractere de cada vez. É importante ressaltar que isso é feito no contexto de localidade atual, o que significa que, se um caractere puder incluir mais de um byte, então, desde que o shell suporte adequadamente caracteres de vários bytes, ele não descartará nenhum bytes, exceto aqueles que não estão no primeiro caractere de seu primeiro argumento.
${1:+":"} return "$((OPTARG=0))"
set "" "" "${1%"${1#?}"}"
Em seguida, ele executa o loop de salvamento, se for necessário, e depois redefine o contexto de local atual para o local C para cada categoria, atribuindo à LC_ALL
variável. Desse ponto em diante, um caractere pode consistir apenas em um único byte; portanto, se houver vários bytes no primeiro caractere de seu primeiro argumento, agora eles deverão ser endereçáveis como caracteres individuais.
LC_ALL=C
É por esse motivo que a segunda metade da função é um while
loop , em oposição a uma sequência de execução individual. Na maioria dos casos, provavelmente será executado apenas uma vez por chamada, mas, se o shell ctbl()
definido corretamente manipular adequadamente caracteres de vários bytes, ele poderá executar um 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)"' "$@"
Observe que a $(ctbl)
substituição do comando acima é avaliada apenas uma vez - eval
quando a função é definida inicialmente - e que para sempre após esse token é substituída pela saída literal da substituição do comando, salva na memória do shell. O mesmo vale para as duas case
substituições de comando padrão. Essa função nunca chama um subshell ou qualquer outro comando. Também nunca tentará ler ou gravar entrada / saída (exceto no caso de alguma mensagem de diagnóstico do shell - o que provavelmente indica um erro) .
Observe também que o teste para continuidade de loop não é simplesmente [ -n "$a" ]
porque, como descobri minha frustração, por algum motivo um bash
shell faz:
char=$(printf \\1)
[ -n "$char" ] || echo but it\'s not null\!
but it's not null!
... e, por isso, comparo explicitamente $a
len a 0 para cada iteração, que, também inexplicavelmente, se comporta de maneira diferente (leia-se: corretamente) .
Ele case
verifica o primeiro byte para inclusão em qualquer uma das quatro seqüências de caracteres e armazena uma referência ao conjunto de bytes $b
. Posteriormente, os quatro primeiros parâmetros posicionais do shell são set
para as cadeias incorporadas eval
e escritas pelo ctbl()
predecessor.
Em seguida, tudo o que resta do primeiro argumento é novamente temporariamente truncado para seu primeiro caractere - que agora deve ser garantido como um único byte. Esse primeiro byte é usado como referência para retirar a cauda da string com a qual correspondeu e a referência $b
é eval
para representar um parâmetro posicional, para que tudo, desde o byte de referência até o último byte na string, possa ser substituído. As outras três strings são retiradas inteiramente dos parâmetros posicionais.
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)))|"
Nesse ponto, o valor do byte (módulo 64) pode ser referenciado como len da string:
str=$(printf '\200\1\2\3\4\5\6\7')
ref=$(printf \\4)
str=${str%"$ref"*}
echo "${#str}"
4
Um pouco de matemática é feito para reconciliar o módulo com base no valor em $b
, o primeiro byte $a
é permanentemente removido e a saída do ciclo atual é anexada a uma pilha pendente de conclusão antes que o loop seja reciclado para verificar se $a
está realmente vazio.
eval " unset LC_ALL a b c;${2%?})'\''"
return "$((${OPTARG%%\**}-1))"
Quando $a
definitivamente está vazio, todos os nomes e estados - com exceção de $OPTARG
- que a função afetada ao longo de sua execução são restaurados ao estado anterior - definidos e não nulos, definidos e nulos ou não definidos - e a saída é salva para $OPTARG
quando a função retornar. O valor de retorno real é um a menos do que o número total de bytes no primeiro caractere de seu primeiro argumento - portanto, qualquer caractere de byte único retorna zero e qualquer caractere de vários bytes retornará mais de zero - e seu formato de saída é um pouco estranho.
O valor ctbl()
salva em $OPTARG
uma expressão aritmética shell válido que, se avaliado, irá simultaneamente definir nomes de variáveis das formas $o1
, $d1
, $o2
, $d2
para decimal e os valores octais de todos os respectivos bytes no primeiro caractere de seu primeiro argumento, mas em última análise avaliar ao total número de bytes em seu primeiro argumento. Eu tinha um tipo específico de fluxo de trabalho em mente ao escrever isso e acho que talvez uma demonstração esteja em ordem.
Costumo encontrar um motivo para separar uma string com o seguinte 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
Provavelmente faço um pouco mais do que apenas imprimir um caractere por linha, mas tudo é possível. Em qualquer caso, eu ainda não encontrei um getopts
que vai fazer corretamente (greve que - dash
é getopts
o faz carbonizar por char, mas bash
definitivamente não) :
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
Está bem. Então eu tentei ...
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
Esse tipo de fluxo de trabalho - o byte por byte / char para o tipo char - é aquele em que geralmente entro ao fazer coisas tty. Na extremidade principal da entrada, você precisa conhecer os valores de caracteres assim que os ler, e os tamanhos (principalmente ao contar colunas) , e os caracteres para serem caracteres inteiros .
E agora eu tenho 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 :: ş
Observe que ctbl()
, na verdade, não define as $[od][12...]
variáveis - nunca tem efeito duradouro em nenhum estado, mas $OPTARG
- apenas coloca a string $OPTARG
que pode ser usada para defini-las - e é assim que obtenho a segunda cópia de cada caractere acima, fazendo isso printf "\\$o1\\$o2"
porque eles são definidos toda vez que eu avalio $(($OPTARG))
. Mas onde eu faço isso, também estou declarando um modificador de tamanho de campo para printf
o %s
formato de argumento de sequência de caracteres e, como a expressão sempre avalia o número total de bytes em um caractere, recebo o caractere inteiro na saída quando:
printf %.2s "$str"
[ "$(printf \\1)" ]|| ! echo but its not null!
enquanto isso , fique à vontade para se familiarizar melhor com a prática de comentários significativos, a menos que você recomende um concurso real ...?
sh
linguagem de comando POSIX . bash
é um bourne novamente supra - definido do mesmo e, em grande parte, um motivador precipitado de grande parte dos cuidados prestados acima a tamanhos de caracteres honrosos amplamente portáteis, auto-expansíveis e com espaço para nome de qualquer tipo. bash
já deve lidar com muito disso, mas o c
idioma printf
estava, e talvez seja, deficiente na capacidade acima fornecida.
Não é um script de shell, mas funciona
awk 'BEGIN{for( i=97; i<=122;i++) printf "%c %d\n",i,i }'
Saída de amostra
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>
você obtém algo como:
mariank@dd903c5n1 ~ $ xxd
û0000000: fb
você sabe que o símbolo que você colou tem código hexadecimal 0xfb
"'A"
está correto enquanto que se usar"A"
, ele vai dizer:A: invalid number
. Parece que é feito no lado printf (ou seja, no shell,"'A"
é de fato 2 caracteres, a'
e aA
. Esses são passados para printf. E, no contexto printf, é convertido no valor ascii de A (e é finalmente impresso como decimal, graças a'%d'
. Use'Ox%x'
para mostrá-lo em hexa ou'0%o'
em octal))