Risposte:
Definire queste due funzioni (generalmente disponibili in altre lingue):
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
e la citazione singolo in "'$1"
do?
Puoi vedere l'intero set con:
$ man ascii
Otterrai tabelle in ottale, esadecimale e decimale.
Se si desidera estenderlo a caratteri UTF-8:
$ perl -CA -le 'print ord shift' 😈
128520
$ perl -CS -le 'print chr shift' 128520
😈
Con bash
, ksh
o zsh
builtins:
$ printf "\U$(printf %08x 128520)\n"
😈
iceweasel
on Debian sid
. Il font come confermato dalla console web di iceweasel è "DejaVu Sans" e ho installato pacchetti ttf-dejavu ttf-dejavu-core ttf-dejavu-extra che provengono da Debian con upstream su dejavu-fonts.org
Funziona bene
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.
esattamente equivalente a:
echo -n "A" | od -An -tuC ### Not all shells honor the '-n'.
echo -n
sopprime la nuova riga finale eliminando la necessità ditr -d "\n"
echo
, non in echi conformi a Unix per esempio. printf %s A
sarebbe quello portatile.
Vado per la soluzione Bash semplice (ed elegante?):
for i in {a..z}; do echo $(printf "%s %d" "$i" "'$i"); done
Perché in uno script puoi usare quanto segue:
CharValue="A"
AscValue=`printf "%d" "'$CharValue"
Notare la singola citazione prima del CharValue. È obbligato ...
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))"
}'
Il primo ctbl()
- in alto lì - corre sempre e solo una volta. Genera il seguente output (che è stato filtrato sed -n l
per motivi di stampabilità) :
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"$
... che sono tutti byte a 8 bit (meno NUL
) , divisi in quattro stringhe tra virgolette suddivise uniformemente a limiti di 64 byte. Le stringhe possono essere rappresentati con intervalli ottali piace \200\1-\77
, \100-\177
, \200-\277
, \300-\377
, dove byte 128 viene utilizzato come segnaposto per NUL
.
Il primo ctbl()
scopo del primo per l'esistenza è generare quelle stringhe in modo che eval
possano definire la seconda ctbl()
funzione con loro letteralmente incorporate in seguito. In questo modo possono essere referenziati nella funzione senza bisogno di generarli di nuovo ogni volta che sono necessari. Quando eval
definisce la seconda ctbl()
funzione, la prima cesserà di essere.
La metà superiore della seconda ctbl()
funzione è per lo più accessoria qui - è progettata per serializzare in modo portabile e sicuro qualsiasi stato di shell corrente che potrebbe influenzare quando viene chiamato. Il ciclo superiore citerà tutte le virgolette nei valori di tutte le variabili che potrebbe voler usare e quindi impilerà tutti i risultati nei suoi parametri posizionali.
Le prime due righe, tuttavia, restituiscono immediatamente 0 e si impostano $OPTARG
sullo stesso se il primo argomento della funzione non contiene almeno un carattere. E se lo fa, la seconda riga tronca immediatamente il suo primo argomento solo al suo primo carattere, perché la funzione gestisce solo un carattere alla volta. È importante sottolineare che lo fa nel contesto locale corrente, il che significa che se un carattere può comprendere più di un singolo byte, quindi, a condizione che la shell supporti correttamente i caratteri multi-byte, non scarterà alcun byte tranne quelli che non si trovano nel primo personaggio del suo primo argomento.
${1:+":"} return "$((OPTARG=0))"
set "" "" "${1%"${1#?}"}"
Quindi esegue il ciclo di salvataggio, se necessario, e successivamente ridefinisce il contesto della locale corrente alla locale C per ogni categoria assegnando alla LC_ALL
variabile. Da questo punto in poi, un carattere può consistere solo di un singolo byte, quindi se nel primo carattere del primo argomento c'erano più byte, questi dovrebbero ora essere indirizzabili come singoli caratteri a sé stanti.
LC_ALL=C
È per questo motivo che la seconda metà della funzione è un while
loop , al contrario di una sequenza di esecuzione singola. Nella maggior parte dei casi probabilmente verrà eseguito solo una volta per chiamata, ma, se la shell in cui ctbl()
è definita gestisce correttamente i caratteri multi-byte, potrebbe essere in 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)"' "$@"
Si noti che la $(ctbl)
sostituzione del comando sopra è valutata solo una volta - da eval
quando la funzione è inizialmente definita - e che per sempre dopo quel token viene sostituita con l'output letterale di quella sostituzione del comando salvato nella memoria della shell. Lo stesso vale per le due case
sostituzioni di comandi del modello. Questa funzione non chiama mai una subshell o nessun altro comando. Inoltre non tenterà mai di leggere o scrivere input / output (tranne nel caso di alcuni messaggi diagnostici della shell - che probabilmente indicano un bug) .
Nota anche che il test per la continuità del loop non è semplice [ -n "$a" ]
, perché, come ho scoperto con mia frustrazione, per qualche motivo una bash
shell fa:
char=$(printf \\1)
[ -n "$char" ] || echo but it\'s not null\!
but it's not null!
... e quindi confronto esplicitamente $a
la len con 0 per ogni iterazione, che, anche inspiegabilmente, si comporta diversamente (leggi: correttamente) .
I case
controlli del primo byte per l'inclusione in una delle nostre quattro corde e memorizza un riferimento al set del byte $b
. Successivamente i primi quattro parametri posizionali della shell sono set
le stringhe incorporate eval
e scritte dal ctbl()
predecessore.
Successivamente, tutto ciò che rimane del primo argomento viene nuovamente troncato temporaneamente al suo primo carattere, che ora dovrebbe essere garantito come un singolo byte. Questo primo byte viene utilizzato come riferimento per rimuovere dalla coda della stringa che ha trovato corrispondenza e il riferimento in $b
è eval
'd per rappresentare un parametro posizionale in modo che tutto, dal byte di riferimento all'ultimo byte nella stringa, possa essere sostituito. Le altre tre stringhe vengono eliminate completamente dai parametri posizionali.
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)))|"
A questo punto il valore del byte (modulo 64) può essere indicato come len della stringa:
str=$(printf '\200\1\2\3\4\5\6\7')
ref=$(printf \\4)
str=${str%"$ref"*}
echo "${#str}"
4
Viene quindi eseguita una piccola matematica per riconciliare il modulo in base al valore in $b
, il primo byte in $a
viene permanentemente rimosso e l'output per il ciclo corrente viene aggiunto a uno stack in attesa di completamento prima che il ciclo ricicli per verificare se $a
è effettivamente vuoto.
eval " unset LC_ALL a b c;${2%?})'\''"
return "$((${OPTARG%%\**}-1))"
Quando $a
definitivamente è vuoto, tutti i nomi e lo stato - ad eccezione di $OPTARG
- che la funzione interessata nel corso della sua esecuzione viene ripristinata al suo stato precedente - sia impostato e non nullo, impostato e nullo o non impostato - e l'output viene salvato a $OPTARG
come ritorna la funzione. Il valore di ritorno effettivo è uno in meno del numero totale di byte nel primo carattere del suo primo argomento - quindi qualsiasi carattere a byte singolo restituisce zero e qualsiasi carattere multibyte restituirà più di zero - e il suo formato di output è un po 'strano.
Il valore ctbl()
salva $OPTARG
è un guscio espressione aritmetica valida che, se valutata, sarà contemporaneamente impostare i nomi variabili delle forme $o1
, $d1
, $o2
, $d2
per decimali e valori ottali di tutti i rispettivi byte nel primo carattere del primo parametro, ma alla fine valutare al totale numero di byte nel suo primo argomento. Avevo in mente un tipo specifico di flusso di lavoro quando scrivevo questo, e penso che forse una dimostrazione sia in ordine.
Trovo spesso un motivo per smontare una stringa 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
Probabilmente faccio un po 'di più che stamparlo un carattere per riga, ma tutto è possibile. In ogni caso, non ho ancora trovato una getopts
che sarà adeguatamente fare (sciopero che - dash
's getopts
lo fa char da char, ma bash
sicuramente non lo fa) :
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
Ok. Quindi ho provato ...
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
Quel tipo di flusso di lavoro - il byte per byte / char per il tipo char - è quello in cui mi capita spesso di fare cose tty. All'estremità iniziale dell'input devi conoscere i valori dei caratteri non appena li leggi, e hai bisogno delle loro dimensioni (specialmente quando conti le colonne) e hai bisogno che i caratteri siano interi .
E così ora ho 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 :: ş
Nota che in ctbl()
realtà non definisce le $[od][12...]
variabili - non ha mai alcun effetto duraturo su nessuno stato ma $OPTARG
- ma inserisce solo la stringa $OPTARG
che può essere utilizzata per definirle - ed è così che ottengo la seconda copia di ogni carattere sopra facendo printf "\\$o1\\$o2"
perché sono impostati ogni volta che valuto $(($OPTARG))
. Ma dove lo faccio sto anche dichiarare un modificatore di lunghezza campo per printf
's %s
formato argomento stringa, e perché l'espressione restituisce sempre il numero totale di byte in un personaggio, ho l'intero carattere sull'uscita quando lo faccio:
printf %.2s "$str"
[ "$(printf \\1)" ]|| ! echo but its not null!
frattempo, non esitate a familiarizzare meglio con la pratica dei commenti significativi, a meno che non raccomandiate un tale concorso reale ...?
sh
linguaggio dei comandi POSIX . bash
è ancora una volta una sovrastima dello stesso, e in gran parte un precipitoso motivatore per gran parte delle cure fornite in precedenza verso dimensioni di caratteri onorevoli ampiamente portatili, autoespandibili e dello spazio dei nomi di qualsiasi tipo. bash
dovrebbe già gestire gran parte di questo, ma la c
lingua printf
era, e forse è, carente rispetto alle capacità sopra fornite.
Non uno script di shell, ma funziona
awk 'BEGIN{for( i=97; i<=122;i++) printf "%c %d\n",i,i }'
Uscita campione
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>
ottieni qualcosa del tipo:
mariank@dd903c5n1 ~ $ xxd
û0000000: fb
sai che il simbolo che hai incollato ha un codice esadecimale 0xfb
"'A"
è corretto, mentre se si utilizza"A"
si dirà:A: invalid number
. Sembra che sia fatto sul lato printf (cioè, nella shell,"'A"
sono effettivamente 2 caratteri, a'
e aA
. Questi sono passati a printf. E nel contesto printf, viene convertito nel valore ascii di A, (e infine viene stampato come decimale grazie al'%d'
. l'utilizzo'Ox%x'
di mostrarlo in hexa o di'0%o'
avere in ottale))