用于获取字母表的 ASCII 值的 Bash 脚本

xmp*_*ate 59 bash ascii bash-script

如何获得字母表的 ASCII 值?

例如,97对于a?

小智 87

定义这两个函数(通常在其他语言中可用):

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

ord() {
  LC_CTYPE=C printf '%d' "'$1"
}
Run Code Online (Sandbox Code Playgroud)

用法:

chr 65
A

ord A
65
Run Code Online (Sandbox Code Playgroud)

  • @dmsk80:+1。对于像我这样认为他们发现错字的其他人:`"'A"` 是正确的,而如果你使用 `"A"`,它会说:`A: invalid number`。它似乎是在 printf 方面完成的(即,在 shell 中,`"'A"` 确实是 2 个字符,一个 `'` 和一个 `A`。它们被传递给 printf。在 printf 上下文中,它被转换到 A 的 ascii 值,(由于 `'%d'`,最终被打印为十进制。使用 `'Ox%x'` 以六进制显示它或使用 `'0%o'` 将它显示在八进制)) (9认同)
  • -1 没有解释它是如何工作的......开玩笑:D,但是这些`printf "\\$(printf '%03o' "$1")"`、`'%03o'`、`LC_CTYPE=C 是做什么的? ` 和 `"'$1"` 中的单引号呢? (4认同)
  • 阅读所有 [常见问题解答 71 中的详细信息](http://mywiki.wooledge.org/BashFAQ/071)。优秀的详细分析。 (3认同)

小智 21

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

$ man ascii
Run Code Online (Sandbox Code Playgroud)

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


小智 18

这个效果很好,

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.
Run Code Online (Sandbox Code Playgroud)

完全等同于:

echo -n "A" | od -An -tuC        ### Not all shells honor the '-n'.
Run Code Online (Sandbox Code Playgroud)

  • 你可以添加一个小的解释吗? (3认同)
  • `echo -n` 抑制尾随换行符,消除对 `tr -d "\n"` 的需要 (2认同)
  • @Gowtham,只有一些 `echo` 的实现,而不是在 Unix 兼容的回声中。`printf %s A` 将是便携式的。 (2认同)

Sté*_*las 14

如果您想将其扩展为 UTF-8 字符(假设您在 UTF-8 语言环境中):

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

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

Run Code Online (Sandbox Code Playgroud)

使用bash,kshzsh内置函数:

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

Run Code Online (Sandbox Code Playgroud)


mik*_*erv 7

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))"
}'
Run Code Online (Sandbox Code Playgroud)

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

ctbl | sed -n l
Run Code Online (Sandbox Code Playgroud)
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))"
}'
Run Code Online (Sandbox Code Playgroud)

...都是 8 位字节(less NUL),分为四个 shell 引用的字符串,在 64 字节边界上均匀分割。字符串可能用八进制范围表示,例如\200\1-\77, \100-\177, \200-\277, \300-\377,其中字节 128 用作 的占位符NUL

第一个ctbl()存在的全部目的是生成这些字符串,以便eval可以定义第二个ctbl()函数,然后将它们逐字嵌入。这样就可以在函数中引用它们,而无需在每次需要时再次生成它们。何时eval定义第二个ctbl()函数,第一个函数将不再存在。

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

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

        ${1:+":"}       return "$((OPTARG=0))"
        set     "" ""   "${1%"${1#?}"}"
Run Code Online (Sandbox Code Playgroud)

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

        LC_ALL=C
Run Code Online (Sandbox Code Playgroud)

正是因为这个原因,函数的后半部分是一个while 循环,而不是一个单独运行的序列。在大多数情况下,它可能每次调用只执行一次,但是,如果ctbl()定义的 shell正确处理多字节字符,它可能会循环。

        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)"'     "$@"
Run Code Online (Sandbox Code Playgroud)

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

另请注意,循环连续性的测试不仅仅是[ -n "$a" ],因为,正如我感到沮丧的那样,由于某种原因,bashshell 会这样做:

char=$(printf \\1)
[ -n "$char" ] || echo but it\'s not null\!
Run Code Online (Sandbox Code Playgroud)
ctbl | sed -n l
Run Code Online (Sandbox Code Playgroud)

...所以我明确地将$a每次迭代的 len 与 0 进行比较,这也令人费解地表现不同(阅读:正确)

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

接下来,第一个参数的剩余部分再次被临时截断为它的第一个字符——现在应该确保它是单个字节。这第一个字节被用作从它相匹配的字符串,并以引用的尾部的参考条带$beval“d来表示的位置参数,以便从基准字节到在串中的最后一个字节的一切可被取代的路程。其他三个字符串完全从位置参数中删除。

               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)))|"
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

$a绝对为空时,所有名称和状态 - 除了$OPTARG- 在整个执行过程中受影响的函数都恢复到它们以前的状态 - 无论是设置而不为空,设置和空还是未设置 - 并保存输出以$OPTARG作为函数返回。实际的返回值比它的第一个参数的第一个字符的总字节数少一个——所以任何单字节字符都返回零,任何多字节字符都将返回大于零——而且它的输出格式有点奇怪。

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

我经常找到一个理由来拆开一个字符串,getopts比如:

str=some\ string OPTIND=1
while   getopts : na  -"$str"
do      printf %s\\n "$OPTARG"
done
Run Code Online (Sandbox Code Playgroud)
s
o
m
e

s
t
r
i
n
g
Run Code Online (Sandbox Code Playgroud)

我可能做的不仅仅是每行打印一个字符,但一切皆有可能。在任何情况下,我还没有找到一个getopts可以正确执行的操作(打那个 - dash一个getopts字符一个字符,但bash绝对不是)

str=??Œœ????????????  OPTIND=1
while   getopts : na  -"$str"
do      printf %s\\n "$OPTARG"
done|   od -tc
Run Code Online (Sandbox Code Playgroud)
 "\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"$
Run Code Online (Sandbox Code Playgroud)

好的。所以我尝试...

str=??Œœ????????????
while   [ 0 -ne "${#str}" ]
do      printf %c\\n "$str"    #identical results for %.1s
        str=${str#?}
done|   od -tc
Run Code Online (Sandbox Code Playgroud)
        ${1:+":"}       return "$((OPTARG=0))"
        set     "" ""   "${1%"${1#?}"}"
Run Code Online (Sandbox Code Playgroud)

这种工作流程 - 字节/字符的字节为字符类型 - 是我在做 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
Run Code Online (Sandbox Code Playgroud)
        LC_ALL=C
Run Code Online (Sandbox Code Playgroud)

请注意,ctbl()实际没有定义的$[od][12...]变量-它从来没有在任何国家任何持久的影响,但$OPTARG-但只有在把字符串$OPTARG可以用来定义他们-这是我得到的每个字符的第二个副本上面做printf "\\$o1\\$o2",因为每次我评估时都会设置它们$(($OPTARG))。但是在我执行此操作的地方,我还将字段长度修饰符声明为printf%s字符串参数格式,并且由于表达式始终计算为字符中的总字节数,因此当我执行以下操作时,我会在输出中获得整个字符:

printf %.2s "$str"
Run Code Online (Sandbox Code Playgroud)

  • 您应该参加一场混淆的 bash 代码竞赛! (3认同)

phu*_*ert 6

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

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

对于在脚本中,您可以使用以下内容:

CharValue="A"
AscValue=`printf "%d" "'$CharValue"
Run Code Online (Sandbox Code Playgroud)

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

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