我使用以下内容进行转换整数到字符和字符到整数在bash中。但我不明白如何printf \\$(printf '%03o' $1)
或printf '%d' "'$1"
工作。请解释如何 printf \\$(printf '%03o' $1)
工作printf '%d'
。
#!/bin/bash
# chr() - converts decimal value to its ASCII character representation
# ord() - converts ASCII character to its decimal value
chr() {
printf \\$(printf '%03o' $1)
}
ord() {
printf '%d' "'$1"
}
ord A
echo
chr 65
echo
答案1
printf '\101'
其中101
是八进制数,输出具有该值的字节。
当发送到 ASCII 终端时,它将被呈现为A
ASCIIA
中的字符 65(八进制 101)和所有 ASCII 兼容的字符集(其中包括大多数现代字符集,但某些 IBM 系统上仍在使用的 EBCDIC 字符集除外)。
在
printf \\$(printf '%03o' $1)
应该写成:
printf "\\$(printf '%03o' "$1")"
因为在类似 Bourne 的 shell 中,保留参数扩展(如$1
)或命令替换 ( $(...)
) 不加引号是 split+glob 运算符,这里不需要它
printf '%03o' "$1"
将数字转换$1
为 3 位八进制printf "\\$(...)"
将该八进制附加到 a\
(\\
双引号内变为\
)并将其传递给,printf
以便它将输出相应的字节值。
请注意,它仅适用于字符集为每个字符一个字节的语言环境(例如iso8859-1
),或者在具有多字节字符集的语言环境中,仅适用于值 0 到 127。
在bash
,
printf '%d\n' "'A"
打印字符的 Unicode 代码点A
(或者至少在 GNU 系统上返回的值mbtowc()
至少是 Unicode 代码点)。
其他一些实现(包括独立的 GNUprintf
实用程序)则返回字符第一个字节的值。
对于基于 ASCII 的系统上的 和 等 ASCII 字符来说A
,这没有任何区别,但对于其他字符来说,这很重要。例如希腊α
字符 (U+03B1) 编码为:
- iso8859-7 中的字节 225(标准希腊单字节字符集)
- UTF-8 中的字节 206 177(类 Unix 系统上最常用的 Unicode 编码)
- GB18030(Unicode 的官方中文编码)中的字节 166 193。
Bashprintf '%d\n' "'α"
将始终输出945
(十六进制的 0x03b1),这是无论语言环境如何(至少在 GNU 系统上)的 Unicode 代码点α
,但其他语言可能会根据语言环境返回 225、206 或 166。
您可以从中看到,对于 ASCII 字符(或值 0 到 127),或者在使用所有字符的字符集(值 0 到 255)的语言环境中,这些chr
和ord
仅仅是彼此相反。iso8859-1
如果ord()
要返回 Unicode 代码点,则相反(打印与 Unicode 代码点对应的字符)将是:
chr() {
printf "\U$(printf %08X "$1")"
}
(假设bash
4.3 或更高版本(\UXXXXXXXX
在 4.2 中添加,但在 4.3 之前对于字符 U+0080 到 U+00FF 无法正常工作))。
然后,在任何语言环境中:
$ ord α
945
$ chr 945
α
或者ord()
返回给定字符的编码字节值(在当前语言环境中):
ord() {
printf %s "$1" | od -An -vtu1
}
并chr()
输出这些字节:
chr() {
printf "$(printf '\\%o' "$@")"
}
然后,例如在 UTF-8 语言环境中:
$ ord α
206 177
$ chr 206 177
α
(你ord α
会给出 945,你chr
会给出 和 的垃圾chr 945
)chr 206 177
。
或者在语言环境中使用iso8859-7
:
$ ord α
225
$ chr 225
α
(你会给出 945,但如果在 GNU 系统上被替换为if 则ord α
可能给出 225 )。printf
/usr/bin/printf
答案2
内部printf '%03o' $1
返回$1
八进制值 (即 65) 的值 (65 -> 101)。
外部printf \\$(..)
打印八进制值表示的字符。
参见man printf
行:
\NNN 八进制值 NNN 字节(1 到 3 位数字)
因为printf '%d' "'$1"
您需要指定'
以指示$1
应将其视为单个字符常量,否则 printf 会抛出错误,指示该值是无效数字。然后使用字符常量的值以printf
十进制格式打印"%d"
答案3
chr
printf \\$(printf '%03o' $1) # 应该是...printf "$(printf '\\%03o' "$1")"
第一个printf
使用第二个的输出来产生一个字符。我们需要逆向工作来解密它。
第二个printf
用于将整数(从$1
)转换为八进制整数('%03o'
格式)。
$ printf '%03o' 12
014
$ printf '%03o' 65
101
$ printf '%03o' 255
377
然后,该结果与 an 的等效项连接起来\
(必须将其加倍或用引号引起来,以避免其对 shell 的特殊含义)。得到的字符串\101
解释为字节的八进制值。但只有最多三位数(在 printf 的大多数 shell 实现中)。仅解释三个八进制数字是因为该类型的格式旨在用于单字节 0-255(或八进制的 0-377)。因此,大多数 printf 实现仅将八进制数转换为一字节。如果该字节表示控制台使用的语言环境中的 ascii 字符,则该字符将被打印出来。
$ printf \\101\\n
A
$ printf \\377\\n
�
要打印受区域设置影响的字符(而不是字节),我们需要:
- 能够生成的 printf多字节正在使用的语言环境中的字符,以及
- 某种形式的表达,我们想要一个字符而不是一个字节,
例如,在 bash(4.3+) 中:
$ printf \\U263A\\n
☺
请注意,printf
可以打印许多数字:
$ printf '%03o\n' 1495195076287004671
122777777777777777777
但只有前三个被接受为八进制数:
$ printf '\122777777777777777777'
R777777777777777777 # Note the R in the front.
秩序
printf '%d' "'$1"
数据(非格式)字符串删除外部引号,生成的字符串以'
.以 a'
或 a开头的字符串"
对于 printf 来说是特殊的。对于这些字符串,如果格式为数字, 这价值的第一个字符打印(以数字格式字符串给出的格式):
$ printf '%d\n' '"A' # decimal (doesn't expand quoted vars '"$var')
65
$ printf '%d\n' "'A" # decimal
65
$ printf '%o\n' "'A" # octal
101
$ printf '%x\n' "'A" # Hexadecimal
41
"'…"
请注意,对于什么的解释存在一些变化第一个字符实现之间(字节或字符,如果是字节,则结果受所使用的区域设置的影响)。