Réponses:
Définissez ces deux fonctions (généralement disponibles dans d'autres langues):
chr() {
[ "$1" -lt 256 ] || return 1
printf "\\$(printf '%03o' "$1")"
}
ord() {
LC_CTYPE=C printf '%d' "'$1"
}
Usage:
chr 65
A
ord A
65
printf "\\$(printf '%03o' "$1")"
, '%03o'
, LC_CTYPE=C
et la citation unique "'$1"
do?
Vous pouvez voir l'ensemble avec:
$ man ascii
Vous obtiendrez des tableaux en octal, hex et décimal.
Si vous souhaitez l'étendre aux caractères UTF-8:
$ perl -CA -le 'print ord shift' 😈
128520
$ perl -CS -le 'print chr shift' 128520
😈
Avec bash
, ksh
ou zsh
intégré:
$ printf "\U$(printf %08x 128520)\n"
😈
iceweasel
sur Debian sid
. La police a confirmé que par la console Web de Iceweasel est "Sans DejaVu" et j'ai TTF-dejavu TTF-dejavu-core packages supplémentaires TTF-dejavu installés qui viennent de Debian avec en amont à dejavu-fonts.org
ctbl()
semble me permettre de l' afficher correctement, et de couper l'omble chevalier de la tête d'une chaîne avec printf
, mais il met 4*((o1=360)>=(d1=240)|(o2=237)>=(d2=159)|(o3=230)>=(d3=152)|(o4=210)>=(d4=136))
en $OPTARG
pour les valeurs d'octets.
Ça marche 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.
exactement équivalent à:
echo -n "A" | od -An -tuC ### Not all shells honor the '-n'.
echo -n
supprime le retour à la ligne, éliminant ainsi le besoin detr -d "\n"
echo
, pas en échos compatibles Unix par exemple. printf %s A
serait le portable.
Je vais pour la solution simple (et élégante?) Bash:
for i in {a..z}; do echo $(printf "%s %d" "$i" "'$i"); done
Dans un script, vous pouvez utiliser les éléments suivants:
CharValue="A"
AscValue=`printf "%d" "'$CharValue"
Notez le guillemet simple avant CharValue. C'est obligé ...
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))"
}'
Le premier ctbl()
- au sommet là-bas - ne court jamais qu'une seule fois. Il génère la sortie suivante (qui a été filtrée à des sed -n l
fins d’imprimabilité) :
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"$
... qui sont tous des octets de 8 bits (moins NUL
) , divisés en quatre chaînes citées par le shell, divisées de manière égale à des limites de 64 octets. Les chaînes peuvent être représentées avec des gammes octal aiment \200\1-\77
, \100-\177
, \200-\277
, \300-\377
où l' octet 128 est utilisé comme lieu porte pour NUL
.
Le but premier de ctbl()
la première existence est de générer ces chaînes de manière à eval
pouvoir définir la seconde ctbl()
fonction avec elles incorporées littéralement par la suite. De cette manière, ils peuvent être référencés dans la fonction sans avoir à les générer à chaque fois que nécessaire. Quand eval
définit-on la deuxième ctbl()
fonction, la première cessera d'être.
La moitié supérieure de la seconde ctbl()
fonction est principalement auxiliaire ici - elle est conçue pour sérialiser de manière portable et en toute sécurité tout état de coque actuel qu'il pourrait affecter lors de son appel. La boucle supérieure citera les guillemets dans les valeurs des variables qu’elle voudra utiliser, puis empilera tous les résultats dans ses paramètres de position.
Les deux premières lignes, cependant, retournent immédiatement immédiatement 0 et sont $OPTARG
identiques si le premier argument de la fonction ne contient pas au moins un caractère. Et si tel est le cas, la deuxième ligne tronque immédiatement son premier argument en son premier caractère uniquement, car la fonction ne traite qu'un caractère à la fois. Il est important de noter que cela se produit dans le contexte local actuel, ce qui signifie que si un caractère peut comprendre plus d’un octet, le shell supportera correctement les caractères multi-octets, il ne supprimera aucun octet, à l’exception de ceux qui ne figurent pas dans la liste. premier caractère de son premier argument.
${1:+":"} return "$((OPTARG=0))"
set "" "" "${1%"${1#?}"}"
Il effectue ensuite la boucle de sauvegarde si nécessaire, puis redéfinit le contexte local en cours en fonction du paramètre régional C pour chaque catégorie en lui affectant une LC_ALL
variable. À partir de ce moment, un caractère ne peut être composé que d'un seul octet. Par conséquent, s'il y avait plusieurs octets dans le premier caractère de son premier argument, il devrait désormais s'agir chacun d'adresses adressables comme des caractères individuels à part entière.
LC_ALL=C
C'est pour cette raison que la seconde moitié de la fonction est une while
boucle , par opposition à une séquence à exécution unique. Dans la plupart des cas, il ne sera probablement exécuté qu'une seule fois par appel, mais si le shell dans lequel ctbl()
est défini correctement gère les caractères multi-octets, il risque de se répéter.
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)"' "$@"
Notez que la $(ctbl)
substitution de commande ci-dessus n'est évaluée qu'une seule fois - eval
lorsque la fonction est définie pour la première fois - et que ce jeton est remplacé pour toujours par la sortie littérale de cette substitution de commande, telle qu'elle est enregistrée dans la mémoire du shell. Il en va de même pour les case
substitutions de commande de deux motifs. Cette fonction n'appelle jamais un sous-shell ou une autre commande. Il ne tentera jamais non plus de lire ou d’écrire des entrées / sorties (sauf dans le cas d’un message de diagnostic du shell - qui indique probablement un bogue) .
Notez également que le test de la continuité de la boucle n’est pas simplement [ -n "$a" ]
, car, comme j’ai trouvé ma frustration, pour une raison quelconque, un bash
shell ne:
char=$(printf \\1)
[ -n "$char" ] || echo but it\'s not null\!
but it's not null!
... et je compare donc explicitement $a
len à 0 pour chaque itération, ce qui, de manière inexplicable, se comporte différemment (lire: correctement) .
Le case
vérifie si le premier octet est inclus dans l’une de nos quatre chaînes et stocke une référence à l’octet défini dans $b
. Ensuite, les quatre premiers paramètres de position du shell set
concernent les chaînes incorporées eval
et écrites par ctbl()
le prédécesseur de.
Ensuite, tout ce qui reste du premier argument est de nouveau temporairement tronqué au premier caractère - qui devrait maintenant être assuré d'être un simple octet. Ce premier octet est utilisé comme référence pour extraire la fin de la chaîne à laquelle il correspond et que la référence $b
est eval
'd pour représenter un paramètre de position afin que tout, de l'octet de référence au dernier octet de la chaîne, puisse être remplacé. Les trois autres chaînes sont entièrement supprimées des paramètres de position.
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)))|"
À ce stade, la valeur de l'octet (modulo 64) peut être référencée comme len de la chaîne:
str=$(printf '\200\1\2\3\4\5\6\7')
ref=$(printf \\4)
str=${str%"$ref"*}
echo "${#str}"
4
Un peu de calcul est ensuite effectué pour réconcilier le module en fonction de la valeur entrée $b
, le premier octet entré $a
est supprimé de façon permanente et la sortie du cycle en cours est ajoutée à une pile en attente d’achèvement avant que la boucle ne soit recyclée pour vérifier si elle $a
est réellement vide.
eval " unset LC_ALL a b c;${2%?})'\''"
return "$((${OPTARG%%\**}-1))"
Lorsqu'il $a
est définitivement vide, tous les noms et tous les états, à l'exception de ce $OPTARG
que la fonction affectée tout au long de son exécution, sont restaurés à leur état précédent (qu'ils soient définis et non nuls, définis et nuls ou non définis) et la sortie est enregistrée. pour $OPTARG
que la fonction retourne. La valeur de retour réelle est inférieure au nombre total d'octets dans le premier caractère de son premier argument - ainsi, tout caractère sur un octet renvoie zéro et tout caractère multi-octets renverra plus que zéro - et son format de sortie est un peu étrange.
La valeur ctbl()
sauve à $OPTARG
un shell valide expression arithmétique que, si une évaluation, seront les noms de variables mis en même temps que des formes $o1
, $d1
, $o2
, $d2
à valeurs décimales et octal de tous les octets respectifs dans le premier caractère de son premier argument, mais finalement évaluer au total nombre d'octets dans son premier argument. J'avais un type de flux de travail spécifique à l'esprit lorsque j'ai écrit ceci, et je pense qu'une démonstration est peut-être nécessaire.
Je trouve souvent une raison de séparer une chaîne de caractères avec 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
Je fais probablement un peu plus que simplement imprimer un caractère par ligne, mais tout est possible. Dans tous les cas, je ne l' ai pas encore trouvé getopts
qui bien faire (grève - dash
« est le getopts
fait carboniser par char, mais bash
ne certainement pas) :
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
D'accord. Alors j'ai essayé ...
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
Ce type de flux de travail - l'octet pour octet / caractère pour caractère de caractère - est celui dans lequel je m'engage souvent lorsque je fais des choses difficiles. Au début de la saisie, vous devez connaître les valeurs des caractères dès que vous les lisez et leur taille (en particulier lors du comptage de colonnes) , ainsi que des caractères entiers .
Et maintenant j'ai 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 :: ş
Notez que ctbl()
cela ne définit pas réellement les $[od][12...]
variables - cela n’a aucun effet durable sur un état, mais $OPTARG
- mais ne met que la chaîne $OPTARG
qui peut être utilisée pour les définir - c’est ainsi que je récupère la deuxième copie de chaque caractère ci-dessus printf "\\$o1\\$o2"
parce que ils sont définis chaque fois que j'évalue $(($OPTARG))
. Mais là où je le fais je déclare également un modificateur de longueur du champ à printf
l » %s
format argument de chaîne, et parce que l'expression évalue toujours au nombre total d'octets dans un caractère, je reçois tout le caractère de la sortie quand je fais:
printf %.2s "$str"
[ "$(printf \\1)" ]|| ! echo but its not null!
attendant , veuillez vous reporter à la question , n'hésitez pas à mieux vous familiariser avec la pratique des commentaires significatifs, à moins que vous ne recommandiez un tel concours ...?
sh
langage de commande POSIX . bash
est encore une fois un élément suprême de la même chose, et en grande partie un facteur de motivation précipité pour la plupart des soins apportés ci-dessus à des tailles de caractère largement portables, expansibles et honorables en espace de nommage de toute sorte. bash
devrait en gérer une grande partie déjà, mais le c
langage printf
était, et est peut-être, déficient de la capacité fournie ci-dessus.
Pas un script shell, mais fonctionne
awk 'BEGIN{for( i=97; i<=122;i++) printf "%c %d\n",i,i }'
Échantillon de sortie
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>
vous obtenez quelque chose comme:
mariank@dd903c5n1 ~ $ xxd
û0000000: fb
vous connaissez le symbole que vous avez collé a un code hexadécimal 0xfb
"'A"
est correcte alors que si vous utilisez"A"
ce dira:A: invalid number
. Il semble que cela soit fait du côté de printf (c’est-à-dire que, dans le shell, il"'A"
y a bien 2 caractères, a'
et aA
. Ceux-ci sont passés à printf. Et dans le contexte de printf, il est converti en valeur ascii de A, en décimal grâce à l ''%d'
. Utilisez - le'Ox%x'
pour l' afficher en hexa ou'0%o'
pour l 'avoir en octal))