Antworten:
Definieren Sie diese beiden Funktionen (normalerweise in anderen Sprachen verfügbar):
chr() {
[ "$1" -lt 256 ] || return 1
printf "\\$(printf '%03o' "$1")"
}
ord() {
LC_CTYPE=C printf '%d' "'$1"
}
Verwendungszweck:
chr 65
A
ord A
65
printf "\\$(printf '%03o' "$1")"
, '%03o'
, LC_CTYPE=C
und das Apostroph in "'$1"
do?
Sie können das gesamte Set sehen mit:
$ man ascii
Sie erhalten Tabellen in Oktal, Hex und Dezimal.
Wenn Sie es auf UTF-8-Zeichen erweitern möchten:
$ perl -CA -le 'print ord shift' 😈
128520
$ perl -CS -le 'print chr shift' 128520
😈
Mit bash
, ksh
oder zsh
builtins:
$ printf "\U$(printf %08x 128520)\n"
😈
iceweasel
am Debian sid
. Die von iceweasels Webkonsole bestätigte Schriftart ist "DejaVu Sans" und ich habe ttf-dejavu-dejavu-Kern-ttf-dejavu-Extra-Pakete installiert, die von Debian mit Upstream bei dejavu-fonts.org
Das funktioniert gut,
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.
genau gleichbedeutend mit:
echo -n "A" | od -An -tuC ### Not all shells honor the '-n'.
echo -n
unterdrückt nachgestellte Zeilenumbrüche und machttr -d "\n"
echo
, zum Beispiel nicht in Unix-kompatiblen Echos. printf %s A
wäre der tragbare.
Ich wähle die einfache (und elegante?) Bash-Lösung:
for i in {a..z}; do echo $(printf "%s %d" "$i" "'$i"); done
Für ein Skript können Sie Folgendes verwenden:
CharValue="A"
AscValue=`printf "%d" "'$CharValue"
Beachten Sie das einfache Anführungszeichen vor dem CharValue. Es ist obligatorisch ...
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))"
}'
Der erste ctbl()
- ganz oben - läuft immer nur einmal. Es wird die folgende Ausgabe generiert (die sed -n l
aus Gründen der Druckbarkeit gefiltert wurde ) :
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"$
... das sind alle 8-Bit-Bytes (weniger NUL
) , die in vier in Anführungszeichen gesetzte Zeichenfolgen unterteilt sind, die gleichmäßig an 64-Byte-Grenzen aufgeteilt sind. Die Saiten können dargestellt werden mit Oktal Bereichen wie \200\1-\77
, \100-\177
, \200-\277
, \300-\377
, wo Byte 128 als Platzhalter verwendet wird NUL
.
Der ctbl()
gesamte Existenzzweck des ersten besteht darin, diese Zeichenfolgen zu generieren, damit eval
die zweite ctbl()
Funktion mit ihnen definiert werden kann, die danach buchstäblich eingebettet ist. Auf diese Weise können sie in der Funktion referenziert werden, ohne dass sie jedes Mal neu generiert werden müssen. Wann eval
wird die zweite ctbl()
Funktion definiert, hört die erste auf zu sein.
Die obere Hälfte der zweiten ctbl()
Funktion ist hier hauptsächlich nebensächlich - sie dient dazu, jeden aktuellen Shell-Status, den sie beim Aufrufen möglicherweise beeinflusst, portabel und sicher zu serialisieren. In der oberen Schleife werden alle Anführungszeichen in den Werten der Variablen angegeben, die möglicherweise verwendet werden sollen. Anschließend werden alle Ergebnisse in ihren Positionsparametern gestapelt.
Die ersten beiden Zeilen geben jedoch sofort 0 zurück und werden auf $OPTARG
den gleichen Wert gesetzt , wenn das erste Argument der Funktion nicht mindestens ein Zeichen enthält. In diesem Fall schneidet die zweite Zeile das erste Argument sofort auf das erste Zeichen ab, da die Funktion jeweils nur ein Zeichen verarbeitet. Wichtig ist, dass dies im aktuellen Gebietsschemakontext der Fall ist. Wenn ein Zeichen mehr als ein einzelnes Byte enthält, werden, sofern die Shell Multibyte-Zeichen ordnungsgemäß unterstützt, keine Bytes außer denjenigen verworfen, die sich nicht in der befinden erstes Zeichen seines ersten Arguments.
${1:+":"} return "$((OPTARG=0))"
set "" "" "${1%"${1#?}"}"
Anschließend wird die Sicherungsschleife ausgeführt, sofern dies überhaupt erforderlich ist, und anschließend wird der aktuelle Gebietsschemakontext für jede Kategorie durch Zuweisen zur LC_ALL
Variablen in das Gebietsschema C neu definiert . Ab diesem Zeitpunkt kann ein Zeichen nur noch aus einem einzigen Byte bestehen. Wenn das erste Zeichen des ersten Arguments mehrere Bytes enthält, sollten diese nun jeweils als einzelne Zeichen für sich adressierbar sein.
LC_ALL=C
Aus diesem Grund ist die zweite Hälfte der Funktion im Gegensatz zu einer einzeln ausgeführten Sequenz eine while
Schleife . In den meisten Fällen wird es wahrscheinlich nur einmal pro Aufruf ausgeführt. Wenn die Shell, in der ctbl()
es definiert ist, jedoch Multibyte-Zeichen ordnungsgemäß verarbeitet, wird möglicherweise eine Schleife ausgeführt.
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)"' "$@"
Beachten Sie, dass die obige $(ctbl)
Befehlsersetzung immer nur einmal ausgewertet wird - bis zum eval
Zeitpunkt der erstmaligen Definition der Funktion - und dass das Token für immer durch die Literalausgabe dieser Befehlsersetzung ersetzt wird, die im Speicher der Shell gespeichert ist. Gleiches gilt für die beiden case
Musterbefehlssubstitutionen. Diese Funktion ruft niemals eine Subshell oder einen anderen Befehl auf. Es wird auch niemals versucht, die Eingabe / Ausgabe zu lesen oder zu schreiben (außer im Fall einer Shell-Diagnosemeldung - die wahrscheinlich auf einen Fehler hinweist) .
Beachten Sie auch, dass der Test für die Schleifenkontinuität nicht einfach ist [ -n "$a" ]
, da, wie ich zu meiner Frustration festgestellt habe, eine bash
Shell aus irgendeinem Grund Folgendes tut:
char=$(printf \\1)
[ -n "$char" ] || echo but it\'s not null\!
but it's not null!
... und so vergleiche ich $a
's len explizit mit 0 für jede Iteration, die sich ebenfalls unerklärlicherweise anders verhält (zu lesen: richtig) .
Das case
prüft das erste Byte auf Aufnahme in einen unserer vier Strings und speichert einen Verweis auf das eingestellte Byte $b
. Danach beziehen set
sich die ersten vier Positionsparameter der Shell auf die Zeichenfolgen, die vom Vorgänger der Shell eingebettet eval
und geschrieben wurden ctbl()
.
Als nächstes wird alles, was vom ersten Argument übrig bleibt, vorübergehend wieder auf sein erstes Zeichen gekürzt - was nun als ein einzelnes Byte sichergestellt werden sollte. Dieses erste Byte wird als Referenz verwendet, um vom Ende der Zeichenfolge, mit der es übereinstimmt, abzulösen, und die Referenz in $b
soll eval
einen Positionsparameter darstellen, damit alles vom Referenzbyte bis zum letzten Byte in der Zeichenfolge ersetzt werden kann. Die anderen drei Zeichenfolgen werden vollständig aus den Positionsparametern entfernt.
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)))|"
An dieser Stelle kann der Wert des Bytes (Modulo 64) als Länge des Strings bezeichnet werden:
str=$(printf '\200\1\2\3\4\5\6\7')
ref=$(printf \\4)
str=${str%"$ref"*}
echo "${#str}"
4
Anschließend wird der Modul anhand des Werts in abgeglichen $b
, das erste Byte in $a
wird dauerhaft entfernt und die Ausgabe für den aktuellen Zyklus wird an einen Stapel angehängt, bis der Vorgang abgeschlossen ist, bevor die Schleife erneut ausgeführt wird, um zu prüfen, ob sie $a
tatsächlich leer ist.
eval " unset LC_ALL a b c;${2%?})'\''"
return "$((${OPTARG%%\**}-1))"
Wenn $a
definitiv leer ist, werden alle Namen und Zustände - mit der Ausnahme $OPTARG
-, dass die während der Ausführung betroffene Funktion in ihrem vorherigen Zustand wiederhergestellt wird - ob gesetzt und nicht null, gesetzt und null oder nicht gesetzt - und die Ausgabe wird gespeichert bis, $OPTARG
wenn die Funktion zurückkehrt. Der tatsächliche Rückgabewert ist eins weniger als die Gesamtzahl der Bytes im ersten Zeichen des ersten Arguments. Jedes einzelne Byte-Zeichen gibt also Null zurück, und jedes Mehrbyte-Zeichen gibt mehr als Null zurück. Das Ausgabeformat ist etwas seltsam.
Der Wert ctbl()
speichert , um $OPTARG
ein gültiges Schal arithmetischer Ausdruck , dass, wenn beurteilt wird , gleichzeitig variable Namen der Formulare festlegen $o1
, $d1
, $o2
, $d2
in dezimalen und oktalen Werten alle entsprechenden Bytes in dem ersten Zeichen des ersten Arguments, aber letztlich auf die Gesamt evaluieren Anzahl der Bytes in seinem ersten Argument. Beim Schreiben hatte ich eine bestimmte Art von Workflow im Sinn, und ich denke, dass eine Demonstration angebracht ist.
Ich finde oft einen Grund, eine Saite zu zerlegen 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
Ich mache wahrscheinlich ein bisschen mehr als nur ein Zeichen pro Zeile, aber alles ist möglich. Auf jeden Fall habe ich noch keinen gefunden getopts
, der das richtig macht (Strike That - dash
das getopts
macht char by char, aber bash
definitiv nicht) :
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. Also habe ich versucht ...
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
Diese Art von Workflow - Byte für Byte / Zeichen für Zeichen - ist einer, auf den ich häufig stoße, wenn ich ein paar Dinge erledige. Am Anfang der Eingabe müssen Sie die Zeichenwerte kennen, sobald Sie sie lesen, und Sie müssen deren Größe (insbesondere beim Zählen von Spalten) kennen . Außerdem müssen Zeichen ganze Zeichen sein.
Und jetzt habe ich 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 :: ş
Beachten Sie, dass ctbl()
die $[od][12...]
Variablen nicht wirklich definiert werden - es hat niemals eine dauerhafte Auswirkung auf einen Zustand, $OPTARG
sondern es wird nur die Zeichenfolge eingefügt $OPTARG
, mit der sie definiert werden können. Auf diese Weise erhalte ich die zweite Kopie der obigen Zeichen, printf "\\$o1\\$o2"
weil Sie werden jedes Mal festgelegt, wenn ich eine Bewertung vornehme $(($OPTARG))
. Aber wo ich es mache, deklariere ich auch einen Feldlängenmodifikator für printf
das %s
String-Argument-Format. Da der Ausdruck immer die Gesamtzahl der Bytes in einem Zeichen ergibt, erhalte ich das gesamte Zeichen bei der Ausgabe, wenn ich Folgendes mache:
printf %.2s "$str"
[ "$(printf \\1)" ]|| ! echo but its not null!
Zwischenzeit. Sie können sich besser mit der sinnvollen Kommentarpraxis vertraut machen, es sei denn, Sie empfehlen einen solchen Wettbewerb.
sh
Befehlssprache. bash
Es ist wieder einmal ein großer Vorteil derselben und zum großen Teil ein eindrucksvoller Motivator für einen Großteil der Sorgfalt, die oben für weithin tragbare, sich selbst erweiternde und namenspace-ehrenwerte Zeichengrößen jeglicher Art geleistet wurde. bash
sollte bereits viel davon bewältigen, aber die c
Sprache printf
war und ist möglicherweise mangelhaft die oben bereitgestellten Fähigkeiten.
Kein Shell-Skript, funktioniert aber
awk 'BEGIN{for( i=97; i<=122;i++) printf "%c %d\n",i,i }'
Beispielausgabe
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>
du bekommst so etwas wie:
mariank@dd903c5n1 ~ $ xxd
û0000000: fb
Sie wissen, dass das Symbol, das Sie eingefügt haben, einen Hex-Code hat 0xfb
"'A"
richtig ist, während , wenn Sie verwenden"A"
es wird sagen:A: invalid number
. Es scheint, als ob es auf der printf-Seite gemacht wurde (dh in der Shell sind"'A"
es tatsächlich 2 Zeichen, a'
und aA
. Diese werden an printf übergeben. Und im printf-Kontext wird es in den ASCII-Wert von A konvertiert (und wird schließlich gedruckt) als dezimaler Dank an die'%d'
. Verwenden Sie'Ox%x'
, um es in hexa zu zeigen oder'0%o'
um es in oktal zu haben))