Answers:
定义这两个功能(通常以其他语言提供):
chr() {
[ "$1" -lt 256 ] || return 1
printf "\\$(printf '%03o' "$1")"
}
ord() {
LC_CTYPE=C printf '%d' "'$1"
}
用法:
chr 65
A
ord A
65
printf "\\$(printf '%03o' "$1")"
,'%03o'
,LC_CTYPE=C
并在单引号"'$1"
做?
如果要将其扩展为UTF-8字符:
$ perl -CA -le 'print ord shift' 😈
128520
$ perl -CS -le 'print chr shift' 128520
😈
随着bash
,ksh
或zsh
内建的:
$ printf "\U$(printf %08x 128520)\n"
😈
iceweasel
在上Debian sid
。由iceweasel的Web控制台证实的字体是“幻觉记忆三世”,我已经安装了TTF-dejavu的TTF-dejavu的核心TTF-dejavu的,额外的软件包,其在来自Debian中上游dejavu-fonts.org
这样很好
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'.
echo -n
抑制尾随换行符,从而无需tr -d "\n"
echo
,例如不使用Unix兼容的回显。printf %s A
将是便携式的。
我要使用简单(优雅)的Bash解决方案:
for i in {a..z}; do echo $(printf "%s %d" "$i" "'$i"); done
对于脚本,可以使用以下命令:
CharValue="A"
AscValue=`printf "%d" "'$CharValue"
注意CharValue之前的单引号。有义务...
printf "%d"
。
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" ]
,因为我感到沮丧的是,由于某种原因,bash
shell会这样做:
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引用$b
是eval
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)))|"
此时,字节的值(模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
会做正确(罢工- dash
的getopts
它被烧焦烧焦,但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"
[ "$(printf \\1)" ]|| ! echo but its not null!
同时参考,除非您建议进行实际的此类比赛,否则请随时更好地了解有意义的评论做法。
sh
命令语言。bash
是伯恩再次 supraset的相同,而在很大程度上是一个陡峭的动力非常重要的对任何形式的广泛便携,自我扩张和命名空间光荣的文字尺寸上面提供的照顾。bash
应该已经处理了很多事情,但是这种c
语言printf
曾经(也许是现在)缺乏上述提供的功能。
不是shell脚本,但是可以
awk 'BEGIN{for( i=97; i<=122;i++) printf "%c %d\n",i,i }'
样品输出
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
konsole
xxd<press enter>
<SHIFT+INSERT><CTRL+D>
你会得到类似:
mariank@dd903c5n1 ~ $ xxd
û0000000: fb
您知道粘贴的符号为十六进制代码 0xfb
"'A"
是正确的,但是如果您使用"A"
它,则会说:A: invalid number
。看来它是在printf一侧完成的(即,在外壳"'A"
中确实是2个字符,a'
和aA
。这些字符被传递给printf。在printf上下文中,它被转换为A的ascii值,(最后被打印以十进制表示,要感谢'%d'
。,用于'Ox%x'
以'0%o'
十六进制显示还是以八进制显示))