Bash脚本获取字母的ASCII值


Answers:


69

定义这两个功能(通常以其他语言提供):

chr() {
  [ "$1" -lt 256 ] || return 1
  printf "\\$(printf '%03o' "$1")"
}

ord() {
  LC_CTYPE=C printf '%d' "'$1"
}

用法:

chr 65
A

ord A
65

7
@ dmsk80:+1。对于像我这样的人,他们认为他们发现了错别字:"'A"是正确的,但是如果您使用"A"它,则会说:A: invalid number。看来它是在printf一侧完成的(即,在外壳"'A"中确实是2个字符,a '和a A。这些字符被传递给printf。在printf上下文中,它被转换为A的ascii值,(最后被打印以十进制表示,要感谢'%d'。,用于'Ox%x''0%o'十六进制显示还是以八进制显示))
Olivier Dulac

3
-1不能解释它是如何工作...开玩笑:d,但认真做这些printf "\\$(printf '%03o' "$1")"'%03o'LC_CTYPE=C并在单引号"'$1"做?
razzak 2014年

1
阅读FAQ 71中的所有详细信息。出色的详细分析。

19

您可以通过以下方式查看整个集合:

$ man ascii

您将获得八进制,十六进制和十进制表格。


还有一个基于debian的发行版的ascii软件包,但是(至少现在)该问题被标记为bash,因此这些对OP毫无帮助。实际上,它已安装在我的系统上,而我从man ascii那里获得的只是它的手册页。

12

如果要将其扩展为UTF-8字符:

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

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

随着bashkshzsh内建的:

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

您是否打算放置方形字符,否则原始字符不会在帖子中显示,而是由方形字符替换。
mtk

1
@mtk,您需要一个显示UTF-8的浏览器以及具有该128520字符的字体。
斯特凡Chazelas

我使用的是最新版Chrome,并且不认为它不支持UTF-8。想知道您使用的是哪种浏览器?
mtk

@mtk,iceweasel在上Debian sid。由iceweasel的Web控制台证实的字体是“幻觉记忆三世”,我已经安装了TTF-dejavu的TTF-dejavu的核心TTF-dejavu的,额外的软件包,其在来自Debian中上游dejavu-fonts.org
斯特凡Chazelas

128520的底数是多少?我自己ctbl()似乎正确使我能够显示它,并从切片用字符串开头的字符printf,但它把4*((o1=360)>=(d1=240)|(o2=237)>=(d2=159)|(o3=230)>=(d3=152)|(o4=210)>=(d4=136))$OPTARG为字节值。
mikeserv

12

这样很好

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.

完全等同于:

echo -n "A" | od -An -tuC        ### Not all shells honor the '-n'.

3
您能否添加一个小的解释?
Bernhard 2013年

tr从输入中删除“ \ n”(换行)。od用于-t dC用于打印十进制字符。
Saravanan 2013年

1
echo -n抑制尾随换行符,从而无需tr -d "\n"
Gowtham

2
@Gowtham,仅使用的某些实现echo,例如不使用Unix兼容的回显。printf %s A将是便携式的。
斯特凡Chazelas

6

我要使用简单(优雅)的Bash解决方案:

for i in {a..z}; do echo $(printf "%s %d" "$i" "'$i"); done

对于脚本,可以使用以下命令:

CharValue="A"
AscValue=`printf "%d" "'$CharValue"

注意CharValue之前的单引号。有义务...


1
您的答案与dsmsk80的答案有何不同?
Bernhard 2013年

1
我对问题的解释是“如何获取字母值的ASCII值”。不是如何定义一个函数来检索一个字符的ASCII值。因此,我的第一个答案是一个简短的单行命令,以获取字母表的ASCII值。
phulstaert

我明白你的意思,但我仍然认为两个答案的底线都是printf "%d"
Bernhard 2013年

2
我同意这是获得结果的过程的关键部分,但我不想假设xmpirate知道“ for for”和范围的使用。如果他想要一个列表,这可能是一个节省时间的;-)。另外,将来的读者可能会发现我的补充内容很有帮助。
phulstaert 2013年

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))"
}'

第一次ctbl()-在顶部-一次只运行一次。它生成以下输出sed -n l出于可打印性的考虑已对其进行过滤)

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"$

...都是8位字节(更少NUL,分为四个用shell引用的字符串,在64位字节的边界处平均分割。琴弦可能与八进制范围等来表示\200\1-\77\100-\177\200-\277\300-\377,其中128字节用作一个占位NUL

第一个ctbl()存在的全部目的是生成那些字符串,以便eval可以定义第二个ctbl()函数,并在其后按字面意义嵌入它们。这样,可以在函数中引用它们,而无需在每次需要它们时都再次生成它们。当eval定义第二个ctbl()功能时,第一个将不再存在。

第二个ctbl()函数的上半部分在这里主要是辅助的-它被设计为可移植并安全地序列化在调用它时可能影响的任何当前shell状态。顶部循环将在可能要使用的任何变量的值中用引号引起来,然后将所有结果堆叠在其位置参数中。

但是,前两行首先立即返回0,并且$OPTARG如果函数的第一个参数不包含至少一个字符,则将其设置为相同。如果这样做的话,第二行立即将其第一个参数截断为仅将其第一个字符截断-因为该函数一次仅处理一个字符。重要的是,它在当前的语言环境中执行此操作,这意味着,如果一个字符可能包含多个字节,那么只要外壳程序正确支持多字节字符,它将不会丢弃任何字节,除非那些字符不在字符中。第一个参数的第一个字符。

        ${1:+":"}       return "$((OPTARG=0))"
        set     "" ""   "${1%"${1#?}"}"

然后,如果有必要,它将执行保存循环,然后,通过分配给LC_ALL变量,将每个区域的当前语言环境重新定义为C语言环境。从这一点开始,一个字符只能由一个字节组成,因此,如果在其第一个参数的第一个字符中有多个字节,则这些字符现在应该可以独立地作为单个字符寻址。

        LC_ALL=C

因此,该函数的后半部分是一个while 循环,而不是单个运行序列。在大多数情况下,每个调用可能只执行一次,但是,如果在其中ctbl()正确定义了外壳程序处理多字节字符,则可能会循环执行。

        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)"'     "$@"

请注意,上述$(ctbl)命令替换仅eval在函数最初定义时进行一次评估-并且在该令牌之后永远被保存在外壳内存中的该命令替换的文字输出替换。两种case模式命令替换也是如此。此函数永远不会调用子shell或任何其他命令。它也绝不会尝试读取或写入输入/输出(除非出现某些外壳诊断消息,否则可能表明存在错误)

还要注意,对循环连续性的测试不是简单的[ -n "$a" ],因为我感到沮丧的是,由于某种原因,bashshell会这样做:

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

but it's not null!

...因此$a,对于每次迭代,我都明确将len与len 比较为0,这也莫名其妙地表现为不同的行为(正确阅读)

case检查检查第一个字节是否包含在我们的四个字符串中的任何一个中,并存储对字节集的引用$b。之后,shell的前四个位置参数是由的前任set嵌入eval和写入的字符串ctbl()

接下来,第一个参数的所有剩余内容将再次被暂时截断为其第一个字符-现在应确保它是一个字节。该第一个字节用作从匹配的字符串的尾部剥离的引用,并且in引用$bevald表示位置参数,因此从引用字节到字符串中最后一个字节的所有内容都可以替换掉。其他三个字符串将从位置参数中完全删除。

               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)))|"

此时,字节的值(模64)可以作为字符串的len引用:

str=$(printf '\200\1\2\3\4\5\6\7')
ref=$(printf \\4)
str=${str%"$ref"*}
echo "${#str}"

4

然后,根据in中的值进行一些数学运算,以调和模数,in $b中的第一个字节$a被永久剥离,并且在循环循环检查是否$a为空之前,将当前循环的输出追加到待完成的堆栈中。

    eval "   unset   LC_ALL  a b c;${2%?})'\''"
    return  "$((${OPTARG%%\**}-1))"

$a绝对为空时,$OPTARG在整个执行过程中受影响的函数的所有名称和状态(例外)将恢复到其先前的状态(无论是否设置为null,set和null或未设置),并保存输出$OPTARG返回到函数。实际的返回值比其第一个参数的第一个字符的字节总数少一个-因此,任何单字节字符都返回零,任何多字节char都返回大于零-并且其输出格式有点奇怪。

的值ctbl()保存到$OPTARG是一个有效的壳算术表达式,如果评价,将同时设置的形式的变量名$o1$d1$o2$d2为十进制和在它的第一参数的第一个字符的所有相应的字节八进制值,但最终评价的总第一个参数中的字节数。在编写本文时,我想到了一种特定的工作流程,并且我认为可能是有必要进行的演示。

我经常会找到理由,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

我可能所做的不仅仅只是每行打印一个字符,还有其他可能。在任何情况下,我还没有找到一个getopts会做正确(罢工- dashgetopts它被烧焦烧焦,但bash没有明确一样)

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

好。所以我尝试了...

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

那种工作流程-字符类型的字节/字符的字节-是我在执行tty时经常遇到的工作流。在输入的最前沿,您需要在阅读char值后立即知道它们,并且需要其大小(尤其是在对列进行计数时),并且需要将字符作为完整字符。

所以现在我有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   ::  ş

请注意,ctbl()实际没有定义的$[od][12...]变量-它从来没有在任何国家任何持久的影响,但$OPTARG-但只有在把字符串$OPTARG可以用来定义他们-这是我得到的每个字符的第二个副本上面做printf "\\$o1\\$o2",因为我每次评估时都会设置它们$(($OPTARG))。但是,在我这样做,我也声明一个字段长度修饰符printf%s字符串参数格式,因为表达式的结果总是以字节的字符总数,我得到的所有文字输出,当我做:

printf %.2s "$str"

您应该参加混淆的Bash代码竞赛!
HelloGoodbye

1
@HelloGoodbye这不是bash代码。这也不会混淆。要了解混淆,请[ "$(printf \\1)" ]|| ! echo but its not null!同时参考,除非您建议进行实际的此类比赛,否则请随时更好地了解有意义的评论做法。
mikeserv '18

不,我不是,我写的只是表示您的代码非常混乱(至少对我而言)的另一种方式,但是也许它不容易理解。如果不是bash,那是什么语言?
HelloGoodbye

@HelloGoodbye-这是POSIX sh命令语言。bash伯恩再次 supraset的相同,而在很大程度上是一个陡峭的动力非常重要的对任何形式的广泛便携,自我扩张和命名空间光荣的文字尺寸上面提供的照顾。bash应该已经处理了很多事情,但是这种c语言printf曾经(也许是现在)缺乏上述提供的功能。
mikeserv

为了简单和易读,我仍然倾向于使用printf“%d”“'$ char”。我很好奇这会给@mikeserv解决方案地址带来什么样的问题?是否有不止一些控制字符会影响返回码(我相信以上评论中他的观点)?
Alex Jansen


2
  • 选择符号,然后按CTRL + C
  • 打开 konsole
  • 并输入: xxd<press enter>
  • 然后按 <SHIFT+INSERT><CTRL+D>

你会得到类似:

mariank@dd903c5n1 ~ $ xxd
û0000000: fb 

您知道粘贴的符号为十六进制代码 0xfb

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.