如何在Bash中通过不同的代码点打印ASCII字符?


Answers:


12

十六进制:

printf '\x4a'

12月:

printf "\\$(printf %o 74)"

十六进制的替代方法:-)

xxd -r <<<'0 4a'

幸运的是,这也适用于awk
Sridhar Sarnobat


6

通常,只要定义为integers:,shell可以理解变量中的十六进制,八进制和十进制数:

$ declare -i v1 v2 v3 v4 v5 v6 v7
$ v1=0112
$ v2=74
$ v3=0x4a
$ v4=8#112
$ v5=10#74
$ v6=16#4a
$ v7=18#gg
echo "$v1 $v2 $v3 $v4 $v5 $v6 $v7"
74 74 74 74 74 74 304

或它们是“算术扩展”的结果:

$ : $(( v1=0112, v2=74, v3=0x4a, v4=8#112, v5=10#74, v6=16#4a, v7=18#gg ))
$ echo "$v1 $v2 $v3 $v4 $v5 $v6 $v7"
74 74 74 74 74 74 304

因此,您只需要一种打印属于变量值的字符的方法。
但是这里有两种可能的方法:

$ var=$((0x65))
$ printf '%b\n' "\\$(printf '0%o' "$var")"
e

$ declare -i var
$ var=0x65; printf '%b\n' "\U$(printf '%08x' "$var")"
e

需要两个printf,一个用于将值转换为十六进制字符串,第二个用于实际打印字符。

第二个将打印任何UNICODE点(如果正确设置了控制台)。
例如:

$ var=0x2603; printf '%b\n' "\U$(printf '%08x' "$var")"

一个雪人。

具有一个UTF-8表示的字符作为f0 9f 90 ae0x1F42E。搜索cow face site:fileformat.info得到它

$ var=0x1F42F; printf '%b\n' "\U$(printf '%08x' "$var")"
🐮

注意:UNICODE方式存在问题,对于4.3之前的bash(在该版本及更高版本中已更正),UNICODE点128和255之间的字符(十进制)可能被错误地打印。


参考文献

第四段内容PARAMETERSman bash

如果变量设置了其整数属性,则即使不使用$((...))扩展,也将值作为算术表达式求值(请参见下面的Arithmetic Expansion)。

在“算术评估”中man bash

前导0的常量被解释为八进制数。前导0x或0X表示十六进制。否则,数字采用[base#] n的形式,其中可选的底数是2到64之间的十进制数,代表算术底数,n是该底数中的数字。如果省略了base#,则使用10为底。大于9的数字依次由小写字母,大写字母@和_表示。如果base小于或等于36,则小写字母和大写字母可以互换使用,以表示10到35之间的数字。


@StéphaneChazelas好吧,代码点不是(总是)字节值。Bash(在4.3之前的版本中)提供代码点的字节值。即:字符é(八进制:351,十进制:233,十六进制:0xE9)打印不正确,printf '\351'因为它打印的字节值0xE9始终为。对于ISO-8859-1可能使用(和表亲)编码的终端,但在utf-8编码的终端中,字节值0xE9应显示为``。续...
艾萨克(Isaac)

@StéphaneChazelas我不是第一个注意到并搜索“ bash 4.2错误编码”的示例。它已从bash 4.3及更高版本进行了更正。
艾萨克(Isaac)2016年

好。现在我明白了您的意思了(根据您的答案的早期版本,我正在使用4.3进行测试)。请注意,它仅支持bash-4.2,不支持bash-4.1 \u(来自zsh)。
斯特凡Chazelas

5

小数:

chr() {
    local c
    for c
    do
        printf "\\$((c/64*100+c%64/8*10+c%8))"
    done
}

chr 74

十六进制:

chr $((16#4a))

该函数可以执行序列:

$ chr 74 75 76; echo
JKL
$

0

您可以使用POSIX Awk stdlib库

$ awklib 'BEGIN {print str_chr(74)}'
J

$ awklib 'BEGIN {print str_chr(+base_conv("4A", 16, 10))}'
J

$ awklib 'BEGIN {print str_chr(+base_conv(112, 8, 10))}'
J

$ awklib 'BEGIN {print str_chr(+base_conv(1001010, 2, 10))}'
J

0

如果您有要转换的数字列表,并且想要避免函数调用和为每个字符创建子外壳,则可以预先定义ascii集:

ascii=$(for x in {0..9} {A..F}; do for y in {0..9} {A..F}; do echo -ne "\x$x$y"; done; done)

请注意,排除了null字符,因此每个字符都偏移1。

然后使用类似这样的东西(假设每行1个数字):

while read c; do out+="${ascii:$c-1:1}"; done <<< "$in"
echo "$out"

0

这是所有使用的转换printf

printf "%o" "'J" # 112 (oct)
printf "%d" "'J" # 74 (dec)
printf "%x" "'J" # 4a (hex)

printf '\112' # J (oct)
printf "\x$(printf %x 74)" # J (dec, requires double conversion)
printf '\x4a' # J (hex)
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.