macOS下printf与bash交互问题:010为何变为0008?
为什么
CF010会被转换成CF0008? 嘿,我来帮你把这个问题拆明白,核心是printf对前导零数字的八进制解析,再加上你对bash参数替换的细节没摸透~
先拆解你的命令逻辑
你用的命令是:
BN=CF010; echo `printf CF%04d.conllu ${BN/CF/}`
这里分两部分看:
1. bash的参数替换${BN/CF/}
这是bash的模式替换(就是你提到的大括号语法的一种,属于参数扩展),作用很简单:把变量BN里第一个匹配到的CF替换成空字符串。
- 当
BN=CF001时,替换后得到001 - 当
BN=CF010时,替换后得到010
2. printf的%04d格式符的坑
%04d的作用是把输入的数字转成4位十进制数,不足位数补零,但这里有个容易踩的隐藏规则:如果输入的数字以0开头,printf会默认把它当成八进制数解析!
八进制里的010等于十进制的8,所以printf %04d 010会输出0008,再加上前面的CF和后缀.conllu,就变成了CF0008.conllu。
而001作为八进制数和十进制数都是1,所以输出正常的CF0001.conllu。
怎么解决这个问题?
要让printf把带前导零的数字当成十进制处理,有两种简单方法:
方法1:用算术扩展强制指定十进制
在参数前加10#,告诉bash这个数是十进制的,不管有没有前导零:
BN=CF010; printf "CF%04d.conllu\n" $((10#${BN/CF/}))
这里$((...))是bash的算术扩展,10#是进制指定符,这样010就会被当成十进制的10,输出CF0010.conllu。
方法2:用字符串处理避免数字解析
如果不想碰进制问题,可以直接用字符串替换来补零,比如用sed:
BN=CF010; echo "$BN" | sed 's/CF\([0-9]*\)/CF000\1/' | sed 's/CF0*\([0-9]\{4\}\)/CF\1/'
这个思路是先在数字前补足够的零,再截取最后4位数字,也能得到正确的CF0010.conllu。
内容的提问来源于stack exchange,提问作者Alexandre Rademaker
相关产品推荐
相关产品推荐

