bash 脚本中 printf 的奇怪问题:“09”和“08”是无效数字,“07”和“06”很好

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8078167/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 21:10:49  来源:igfitidea点击:

Bizarre issue with printf in bash script:"09" and "08" are invalid numbers, "07" and "06" are fine

bashshellprintf

提问by Richard

This is my bash script - I just want to left-pad a set of numbers with zeroes:

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

printf "%04d" "09"
printf "%04d" "08"
printf "%04d" "07"
printf "%04d" "06"

Output:

输出:

./rename.sh: line 3: printf: 09: invalid number 
0000
./rename.sh: line 4: printf: 08: invalid number 
0000 
0007
0006

What...?

什么...?

Only 09 and 08 are causing the problem: every other number in my sequence seems to be OK.

只有 09 和 08 导致了问题:我序列中的所有其他数字似乎都可以。

回答by glglgl

If you have your "09"in a variable, you can do

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

a="09"
echo "$a"
echo "${a#0}"
printf "%04d" "${a#0}"

Why does this help? Well, a number literal starting with 0but having no xat the 2nd place is interpreted as octal value.

为什么这有帮助?好吧,以 开头0x在第二位没有的数字文字被解释为八进制值。

Octal value only have the digits 0..7, 8and 9are unknown.

八进制值只有数字0.. 78并且9是未知的。

"${a#0}"strips one leading 0. The resulting value can be fed to printfthen, which prints it appropriately, with 0prefixed, in 4 digits.

"${a#0}"带一个领先0。结果值可以输入printfthen,然后0以 4 位数字的前缀适当地打印它。

If you have to expect that you get values such as "009", things get more complicated as you'll have to use a loop which eliminates all excess 0s at the start, or an extglobexpression as mentioned in the comments.

如果您必须期望获得诸如 的值"009",事情会变得更加复杂,因为您必须使用一个循环来消除开始时所有多余的0s,或者使用extglob注释中提到的表达式。

回答by Oliver Charlesworth

Numbers beginning with "0" are treated as octal (i.e. base-8). Therefore, "8" and "9" aren't valid digits.

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

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

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

This behaviour is inherited from languages like C.

这种行为是从 C 等语言继承而来的。

回答by morgant

Bash's numeric arithmetic evaluation syntax (( ... ))can convert to base 10 (therefor ensuring correct interpretation) with the following syntax: (( 10#$var )). Or, in the case of a raw number: (( 10#08 )). Very simple & clean and can be used anywhere you're sure the base should be 10, but can't guarantee a leading zero won't be included.

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

So, in your example it would be as follows:

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

printf "%04d\n" $(( 10#09 ))
printf "%04d\n" $(( 10#08 ))
printf "%04d\n" $(( 10#07 ))
printf "%04d\n" $(( 10#06 ))

Producing the following output:

产生以下输出:

0009
0008
0007
0006

With this syntax, since you're then working with the value of the variable instead of variable itself, incrementors (( var++ ))& decrementors (( var-- ))won't work, but can still be relatively cleanly implemented as var=$(( 10#var + 1 ))and var=$(( 10#var - 1 )), respectively.

使用此语法,由于您随后使用的是变量的值而不是变量本身,因此增量器(( var++ ))和减量器(( var-- ))将不起作用,但仍然可以分别相对干净地实现为var=$(( 10#var + 1 ))var=$(( 10#var - 1 ))

I first encountered this solution here, but this answer to a similar Stack Overflow questionalso demonstrates it.

我第一次在这里遇到了这个解决方案,但是这个对类似 Stack Overflow 问题的回答也证明了这一点。

回答by Ross Aiken

Floating point is handled differently:

浮点的处理方式不同:

printf "%04.f" "009"

This gives the correct output, without dealing with any fancy bashisms (per @Oli Charlesworth's answer, 0*** is treated as octal, but I believe that Bash ignores octal/hex identifiers for floating-point numbers)

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

回答by Nathan Fellman

Just to add to Oli's answer, in order to pad a number with zeroes it is enough to put a 0after the %, as you did:

只是添加到Oli 的答案中,为了用零填充一个数字,就像你所做的那样,0在 之后放置 a 就足够了%

printf "%04d" "9"

printf "%04d" "9"

回答by carlo

Adding *makes shell parameter expansion matching greedy (see, for example, Shell Tipps: use internal string handling)!

添加*使 shell 参数扩展匹配贪婪(例如,请参阅Shell Tipps:使用内部字符串处理)!

# strip leading 0s
- a="009"; echo ${a##0}
+ a="009"; echo ${a##*0}

回答by F. Hauri

Why did “09” and “08” are invalid numbers, “07” and “06” are fine

为什么“09”和“08”是无效数字,“07”和“06”是好的

Because preceding integer by 0is a conventional shortcut meaning number is octal.

因为前面的 integer by0是一个传统的快捷方式,意味着number 是八进制

printf "%d\n" 010 0100
8
64

printf "%o\n" 8 64 0100
10
100
100
printf "%04o\n" 8 64 0100
0010
0100
0100

How to work with this?

如何处理这个问题?

Using bash integer: preceding number by 10#

使用 bash 整数:前面的数字由 10#

Under bash, you could precise by this way, which baseis used for number

在bash下,你可以通过这种方式精确,哪个基数用于数字

echo $(( 10#0100))
100
echo $(( 10#0900))
900
echo $(( 10#0900 ))
900

and

echo $(( 8#100 ))
64
echo $(( 2#100 ))
4

Of course!

当然!

There are only 10 types of people in the world: those who understand binary, and those who don't.

世界上只有10种人:懂二进制的和不懂二进制的。

Using bashParameter Expansion

使用bash参数扩展

For this, you have to use a variable

为此,您必须使用一个变量

a=09
echo ${a#0}
9

This will work fine until you just have only one 0to drop.

这将正常工作,直到您只能0丢弃一个。

a=0000090

echo ${a#*0}
000090
echo ${a##0}
000090

For this, you coul use extglobfeature:

为此,您可以使用extglob功能:

echo ${a##*(0)}
0000090
shopt -s extglob
echo ${a##*(0)}
90

Using floating pointwith printf

使用floating pointprintf

printf "%.0f\n" 09
9

I like using -voption of printffor setting some variable:

我喜欢使用-v选项printf来设置一些变量:

printf -v a %.0f 09
echo $a
9

or even if

或者即使

a=00090
printf -v a %.0f $a
echo $a
90

回答by John Kolvereid

Much simpler to convert from octal to decimal:

从八进制转换为十进制要简单得多:

nm=$((10#$nm))

回答by SanjeethBoddi

if a=079first, remove the trailing zeros by

如果a=079首先,删除尾随零

a=`echo $a | sed 's/^0*//'`

then do the zero-padding

然后做零填充

printf "%04d" $a