Bash script per ottenere valori ASCII per l'alfabeto


Risposte:


70

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

7
@ dmsk80: +1. Per altri come me che pensano di individuare un errore di battitura: "'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 a A. 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))
Olivier Dulac

3
-1 per non spiegare come funziona ... scherzando: D, ma sul serio che cosa questi printf "\\$(printf '%03o' "$1")", '%03o', LC_CTYPE=Ce la citazione singolo in "'$1"do?
razzak,

1
Leggi tutti i dettagli nelle FAQ 71 . Un'eccellente analisi dettagliata.

19

Puoi vedere l'intero set con:

$ man ascii

Otterrai tabelle in ottale, esadecimale e decimale.


C'è anche un pacchetto ascii per le distribuzioni basate su debian, ma (almeno ora) la domanda è taggata come bash, quindi questi non aiuterebbero l'OP. In effetti, è installato sul mio sistema e tutto ciò che ottengo da man ascii è la sua pagina man.
Joe,

12

Se si desidera estenderlo a caratteri UTF-8:

$ perl -CA -le 'print ord shift' 😈
128520

$ perl -CS -le 'print chr shift' 128520
😈

Con bash, ksho zshbuiltins:

$ printf "\U$(printf %08x 128520)\n"
😈

Hai intenzione di mettere un carattere di casella quadrata altrimenti il ​​carattere originale non viene visualizzato nel post e viene sostituito da un carattere di casella quadrata.
mtk,

1
@mtk, è necessario un browser che visualizzi UTF-8 e un carattere con quel carattere 128520 .
Stéphane Chazelas,

Sono su Chrome più recente e non credo che non supporti UTF-8. Vuoi sapere su quale browser ti trovi?
mtk,

@mtk, iceweaselon 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
Stéphane Chazelas

qual è la base di 128520? la mia ctbl()sembra consentire correttamente me per visualizzarlo, e per tagliare il carattere dalla testa di una stringa con printf, ma si mette 4*((o1=360)>=(d1=240)|(o2=237)>=(d2=159)|(o3=230)>=(d3=152)|(o4=210)>=(d4=136))in $OPTARGper i valori di byte.
Mikeserv,

12

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'.

3
Puoi forse aggiungere una piccola spiegazione?
Bernhard,

tr per rimuovere "\ n" (nuova riga) dall'input. od è usato per -t dC è per stampare in caratteri decimali.
Saravanan,

1
echo -nsopprime la nuova riga finale eliminando la necessità ditr -d "\n"
Gowtham,

2
@Gowtham, solo con alcune implementazioni di echo, non in echi conformi a Unix per esempio. printf %s Asarebbe quello portatile.
Stéphane Chazelas,

6

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 ...


1
In che modo la tua risposta è diversa dalla risposta di dsmsk80?
Bernhard,

1
La mia interpretazione della domanda è "come ottenere i valori ASCII per i valori dell'alfabeto". Non come definire una funzione per recuperare il valore ASCII per un carattere. Quindi la mia prima risposta è un breve comando a una riga per ottenere i valori ASCII per l'alfabeto.
phulstaert,

Ottengo il tuo punto, ma penso ancora che la linea di fondo di entrambe le risposte sia printf "%d".
Bernhard,

2
Sono d'accordo che questa è una parte cruciale del processo per arrivare al risultato, ma non volevo supporre che xmpirate fosse a conoscenza del "for i in" e dell'uso di un intervallo. Se voleva un elenco, questo potrebbe essere un vero risparmio di tempo ;-). Inoltre, i futuri lettori potrebbero trovare utili le mie aggiunte.
phulstaert,

6
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 lper 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 evalpossano 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 evaldefinisce 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 $OPTARGsullo 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_ALLvariabile. 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 evalquando 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 casesostituzioni 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 bashshell fa:

char=$(printf \\1)
[ -n "$char" ] || echo but it\'s not null\!

but it's not null!

... e quindi confronto esplicitamente $ala len con 0 per ogni iterazione, che, anche inspiegabilmente, si comporta diversamente (leggi: correttamente) .

I casecontrolli 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 setle stringhe incorporate evale 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 $aviene 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 $adefinitivamente è 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 $OPTARGcome 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, $d2per 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 getoptslike:

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 getoptsche sarà adeguatamente fare (sciopero che - dash's getoptslo fa char da char, ma bashsicuramente 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 $OPTARGche 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 %sformato 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"

Dovresti competere in un contest offuscato di codice bash!
Ciao Arrivederci

1
@Ciao, arrivederci, questo non è codice bash . né questo è offuscato. per vedere l'offuscamento, si prega di fare riferimento nel [ "$(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 ...?
Mikeserv,

No, non lo so, quello che ho scritto era solo un altro modo per dire che il tuo codice è molto confuso (almeno per me), ma forse non doveva essere facilmente comprensibile. Se non è bash, allora che lingua è?
Ciao Arrivederci

@HelloGoodbye - questo è il shlinguaggio 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. bashdovrebbe già gestire gran parte di questo, ma la clingua printfera, e forse è, carente rispetto alle capacità sopra fornite.
Mikeserv,

Sono ancora propenso a usare printf "% d" "'$ char" per semplicità e leggibilità. Sono curioso di sapere che tipo di problemi questo mi espone agli indirizzi della soluzione di @Mikeserv? Esiste qualcosa di più di alcuni caratteri di controllo che influenzano il codice di ritorno (che credo sia stato il suo punto nel commento sopra)?
Alex Jansen,

3

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

2
  • seleziona il simbolo, quindi premi CTRL + C
  • Aperto konsole
  • e digitare: xxd<press enter>
  • quindi premere <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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.