如何将bash中的整数序列零填充以使它们的宽度相同?


426

我需要循环一些值,

for i in $(seq $first $last)
do
    does something here
done

对于$first$last,我需要将其固定长度为5。因此,如果输入为1,则需要在前面加上零,使其变为0000199999例如,它一直循环播放,但是长度必须为5。

如:000020004200212012312等等。

关于我该怎么做的任何想法?


22
一个好的答案可以是:序列-含1个99999选项-w输出保持不变lenght,填充最短的数字为0
布鲁诺·冯·巴黎


这里推荐许多答案,for variable in $(something to generate the numbers); do ...但是当数字列表很长时,这是有问题的。使用起来效率更高something to generate the numbers | while read -r variable; do ...。另请参见mywiki.wooledge.org/DontReadLinesWithFor,其中讨论了从文件等中读取行,但某些参数也适用于此。
人间

Answers:


710

在您的特定情况下,虽然在输出列表时使用-f标志来seq使其格式化数字可能是最容易的。例如:

for i in $(seq -f "%05g" 10 15)
do
  echo $i
done

将产生以下输出:

00010
00011
00012
00013
00014
00015

更一般而言,bash具有printf内置功能,因此您可以按如下所示用零填充输出:

$ i=99
$ printf "%05d\n" $i
00099

您可以使用该-v标志将输出存储在另一个变量中:

$ i=99
$ printf -v j "%05d" $i
$ echo $j
00099

请注意,printf该格式支持的格式略有不同,seq因此您需要使用%05d而不是%05g


5
%05g而不是%05d为我修复它。 “ 00026”给我“ 00022”。谢谢!
Ed Manet

12
很棒的综合答案。一个小的更正:严格来说,seq支持char格式的子集。该printf支撑件(以及子集包括g,但不是d)。字符d和的行为的g细微差别在于,d将- 0前缀数字字符串解释为八进制数字并将其转换为十进制,而g将它们视为十进制。(@EdManet:这就是为什么'00026'用变为'00022'的原因d)。还有一件事值得一提:seq -w不会自动根据所产生的数量最大输出数量的零填充。
mklement0

这有助于我生成十六进制字符列表。for (( i = 0; i <= 0xffffffff; i++ )) do printf "%08x\n" $i ; done >> hex.txt产生一个8个字符的十六进制列表。谢谢。
2014年

seq --help:“ FORMAT必须适合于打印一个'double'类型的参数;如果FIRST,INCREMENT和LAST都是具有最大精度PREC的定点十进制数字,则默认为%.PRECf,否则为%g。可能的转换说明符efgaEFGA
x-yuri

133

您仍然可以轻松完成

for i in {00001..99999}; do
  echo $i
done

16
这在Bash版本4.1.2(CentOS 6)上有效,但在版本3.2.25(CentOS 5)中失败。我确实同意这是一种更美观的方式!
Mike Starov 2013年

1
可以,但是在我的bash上不给零填充字符串
user2707001 17-10-19

在Mac(Bash 4.4)上,这不会填充数字。在CentOS和SuSE上,效果很好!
Stefan Lasiewski

这在使用Bash的macOS上运行良好4.4.23(1)-release
Whymarrh

可悲的是,它也无法在OS X上的Bash 5上运行
gotofritz

91

如果序列的末尾具有最大填充长度(例如,如果您需要5位数字且命令为“ seq 1 10000”),则可以对序列使用“ -w”标志-它将自身添加填充。

seq -w 1 10

生产

01
02
03
04
05
06
07
08
09
10

7
这显然是正确的答案。为什么投票这么少?😳–
adius

5
容易,因为填充取决于您要达到的最大数量。如果这是一个参数,您将永远不知道最终会有多少个零
guillem

5
这不会给出指定的固定长度,而只是给出所有相同长度的结果。
Ceisc


18

使用非常简单 printf

[jaypal:~/Temp] printf "%05d\n" 1
00001
[jaypal:~/Temp] printf "%05d\n" 2
00002

12

这样使用awk:

awk -v start=1 -v end=10 'BEGIN{for (i=start; i<=end; i++) printf("%05d\n", i)}'

输出:

00001
00002
00003
00004
00005
00006
00007
00008
00009
00010

更新:

作为纯bash替代方案,您可以执行以下操作以获得相同的输出:

for i in {1..10}
do
   printf "%05d\n" $i
done

这种方式可以避开使用外部程序seq不可用 * nix上的所有的味道。


1
我们可以使用awk's BEGIN语句,这样我们就不必使用heredoc赋值。
jaypal singh 2012年

@JaypalSingh:谢谢队友,这很不错。更新了我的答案。
anubhava 2012年

9

我用比我需要更多的数字(零)填充输出,然后使用tail来仅使用我要寻找的数字。请注意,您必须在尾部使用'6'来获取最后五个数字:)

for i in $(seq 1 10)
do
RESULT=$(echo 00000$i | tail -c 6)
echo $RESULT
done

如果要在tail的-c参数中使用正确的位数,则可以使用'echo -n 00000 $ i',因为这会阻止它输出换行符,这是char tail返回的其中之一,因此需要在这个答案中更高。根据您要执行的操作,换行符(如果保留的话)可能会影响结果。
Ceisc

使用外部过程修整循环中的变量效率很低。您可以改用Shell的参数扩展功能。在Bash中,有一种按索引提取特定子字符串的功能,或者您可以使用前缀和后缀替换使用与所需宽度匹配的模式,尽管这需要一些来回操作。
人间

4

如果需要N位数字,请添加10 ^ N并删除第一位数字。

for (( num=100; num<=105; num++ ))
do
  echo ${num:1:3}
done

输出:

01
02
03
04
05

2

这也将起作用:

for i in {0..9}{0..9}{0..9}{0..9}
do
  echo "$i"
done

不错的解决方案!干净,易于阅读,无需其他程序。
乔纳森·克罗斯

2

另一种方式 :

zeroos="000"
echo 

for num in {99..105};do
 echo ${zeroos:${#num}:${#zeroos}}${num}
done

转换任何数字的简单函数是:

function leading_zero(){

    local num=$1
    local zeroos=00000
    echo ${zeroos:${#num}:${#zeroos}}${num} 

}

0

1.)创建一个从1到1000的数字序列“ seq”,并固定宽度“ -w”(宽度由结束数字的长度确定,在这种情况下,数字为1000的4位数字)。

2.)另外,使用'sed -n'选择想要的数字(在这种情况下,我们选择数字1-100)。

3.)“回显”每个数字。数字存储在变量“ i”中,可使用“ $”访问。

优点:这段代码很干净。

缺点:'seq'并非对所有Linux系统都是本地的(据我了解)

for i in `seq -w 1 1000 | sed -n '1,100p'`; 
do 
    echo $i; 
done

0

如果您只是在用0填充数字以达到固定长度之后,只需添加10的最接近倍数即可。2位数字,加10 ^ 2,然后在显示输出之前删除前1个数字。

该解决方案适用于使用for循环填充/格式化任意长度的单个数字或整个数字序列。

# Padding 0s zeros:
# Pure bash without externals eg. awk, sed, seq, head, tail etc.
# works with echo, no need for printf

pad=100000      ;# 5 digit fixed

for i in {0..99999}; do ((j=pad+i))
    echo ${j#?}
done

在Mac OSX 10.6.8,Bash版本3.2.48上测试


0

不使用外部进程派生的一种方法是字符串操作,在一般情况下,它看起来像这样:

#start value
CNT=1

for [whatever iterative loop, seq, cat, find...];do
   # number of 0s is at least the amount of decimals needed, simple concatenation
   TEMP="000000$CNT"
   # for example 6 digits zero padded, get the last 6 character of the string
   echo ${TEMP:(-6)}
   # increment, if the for loop doesn't provide the number directly
   TEMP=$(( TEMP + 1 ))
done

这在WSL上也非常有效,因为分叉是一项非常繁重的操作。我有一个110000个文件列表,使用printf "%06d" $NUM了1分钟,上面的解决方案在大约1秒钟内运行。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.