在bash脚本中printf的奇怪问题:"09"和"08"是无效数字,"07"和"06"都没问题

Ric*_*ard 27 bash shell printf

这是我的bash脚本 - 我只想用零填充一组数字:

printf "%04d" "09"
printf "%04d" "08"
printf "%04d" "07"
printf "%04d" "06"
Run Code Online (Sandbox Code Playgroud)

输出:

./rename.sh: line 3: printf: 09: invalid number 
0000
./rename.sh: line 4: printf: 08: invalid number 
0000 
0007
0006
Run Code Online (Sandbox Code Playgroud)

什么...?

只有09和08导致问题:我序列中的每个其他数字似乎都没问题.

glg*_*lgl 29

如果你有"09"变量,你可以做

a="09"
echo "$a"
echo "${a#0}"
printf "%04d" "${a#0}"
Run Code Online (Sandbox Code Playgroud)

为什么这有帮助?好吧,从第二位开始0但没有x第二位的数字文字被解释为八进制值.

八进制值只有数字0.. 7,8并且9未知.

"${a#0}"剥一个领先0.结果值可以输入到printf当时,并0以4位数字的前缀对其进行适当打印.

如果你不得不期望得到诸如此类的值,那么"009"事情会变得更复杂,因为你必须使用一个循环来消除0开头的所有多余的s,或者extglob注释中提到的表达式.

  • 为什么在没有任何进一步通知的情况下“-1”到底出了什么问题?! (3认同)
  • 是的,对。可以这样写:`shopt -s extglob; 回声$ {a ## +(0)}` (2认同)
  • 来自我的 +1,因为您的解决方案确实有很大帮助,但我确实有点怀疑无法解释的 bash 变量替换对于将来挖掘这个问题的读者是否具有任何教育价值。澄清一下,OP 的错误来自这样一个事实,即 printf 将以 0 开头的数字解释为八进制,这在 00-07 期间确实可以正常工作。因此,建议在将数字交给 printf 之前去掉前导零。 (2认同)
  • @unixtippse您说得对,因此我现在添加了一些解释。 (2认同)

Oli*_*rth 20

以"0"开头的数字被视为八进制(即基数为8).因此,"8"和"9"不是有效数字.

请参阅http://www.gnu.org/software/bash/manual/bashref.html#Shell-Arithmetic.

此行为继承自C语言.

  • 谢谢,但是如何删除前导零?出于相同的原因,我不能使用`printf“%d”“ 08”`!(上面的示例大大简化了我需要做的事情-实际上参数是一个参数,因此我不能每次都手动编辑它,我需要一种编程的方式来去除前导零,然后再添加它们... ) (2认同)

mor*_*ant 20

bash的数字算术评估语法(( ... ))可以转换为10进制(为此确保正确的解释)的语法如下:(( 10#$var )).或者,在原始数字的情况下:(( 10#08 )).非常简单和干净,可以在任何你确定基数应该是10的地方使用,但不能保证不包括前导零.

因此,在您的示例中,它将如下所示:

printf "%04d\n" $(( 10#09 ))
printf "%04d\n" $(( 10#08 ))
printf "%04d\n" $(( 10#07 ))
printf "%04d\n" $(( 10#06 ))
Run Code Online (Sandbox Code Playgroud)

产生以下输出:

0009
0008
0007
0006
Run Code Online (Sandbox Code Playgroud)

有了这个语法,因为你再与变量,而不是变量本身的价值工作,incrementors (( var++ ))&decrementors (( var-- ))将无法正常工作,但是仍然可以相对清晰地为实现var=$(( 10#var + 1 ))var=$(( 10#var - 1 ))分别.

这里第一次遇到这个解决方案,但这个类似Stack Overflow问题的答案也证明了这一点.

  • 请注意,这在 bash 中有效,但在 dash(默认 ubuntu shell)中无效。在破折号中,出现以下错误:“算术表达式:期待 EOF” (2认同)

Ros*_*ken 5

浮点的处理方式不同:

printf "%04.f" "009"
Run Code Online (Sandbox Code Playgroud)

这样就可以给出正确的输出,而无需处理任何花哨的bashisms(根据@Oli Charlesworth的回答,0 ***被视为八进制,但我相信Bash忽略了浮点数的八进制/十六进制标识符)