我需要循环一些值,

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

对于$first和$last,我需要它的固定长度为5。所以如果输入是1,我需要在前面加上0,这样它就变成了00001。例如,它循环到99999,但长度必须是5。

例如:00002,00042,00212,12312等等。

你知道我该怎么做吗?


当前回答

博士TL;

$ seq 1 10 | awk '{printf("%05d\n", $1)}'

输入(模式1。慢):

$ seq 1 10 | xargs -n 1 printf "%05d\n"

输入(模式2。快):

$ seq 1 10 | awk '{printf("%05d\n", $1)}'

输出(每种情况下的结果相同):

00001
00002
00003
00004
00005
00006
00007
00008
00009
00010

解释

我想建议以上的模式。这些实现可以作为命令使用,以便我们可以轻松地再次使用它们。在这些命令中,您需要关心的是转换后的数字的长度。(比如把数字%05d改成%09d。)另外,它也适用于其他解决方案,如以下。这个示例太依赖于我的环境,所以您的输出可能不同,但我认为您可以很容易地看出它的有用性。

$ wc -l * | awk '{printf("%05d\n", $1)}'
00007
00001
00001
00001
00013
00017
00001
00001
00001
00043

就像这样:

$ wc -l * | awk '{printf("%05d\n", $1)}' | sort | uniq
00001
00007
00013
00017
00043

此外,如果以这种方式编写,我们还可以异步执行命令。(我找到了一篇不错的文章: https://www.dataart.com/en/blog/linux-pipes-tips-tricks)

免责声明:我不确定这一点,我不是*nix专家。

性能测试:

超级慢:

$ time seq 1 1000 | xargs -n 1 printf "%09d\n" > test
seq 1 1000  0.00s user 0.00s system 48% cpu 0.008 total
xargs -n 1 printf "%09d\n" > test  1.14s user 2.17s system 84% cpu 3.929 total

相对速度:

for i in {1..1000}
do
   printf "%09d\n" $i
done
$ time sh k.sh > test
sh k.sh > test  0.01s user 0.01s system 74% cpu 0.021 total


for i in {1..1000000}
do
   printf "%09d\n" $i
done
$ time sh k.sh > test
sh k.sh > test  7.10s user 1.52s system 99% cpu 8.669 total

快速:

$ time seq 1 1000 | awk '{printf("%09d\n", $1)}' > test
seq 1 1000  0.00s user 0.00s system 47% cpu 0.008 total
awk '{printf("%09d\n", $1)}' > test  0.00s user 0.00s system 52% cpu 0.009 total


$ time seq 1 1000000 | awk '{printf("%09d\n", $1)}' > test
seq 1 1000000  0.27s user 0.00s system 28% cpu 0.927 total
awk '{printf("%09d\n", $1)}' > test  0.92s user 0.01s system 99% cpu 0.937 total

如果必须实现更高性能的解决方案,可能需要其他技术,而不是使用shell脚本。

其他回答

使用printf非常简单

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

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

#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上也能很好地工作,在WSL中,分叉是一个非常繁重的操作。我有一个110000个文件列表,使用printf“%06d”$NUM花了1分钟多,上面的解决方案在1秒左右运行。

像这样使用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版本中都不可用。

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

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

将产生以下输出:

00010
00011
00012
00013
00014
00015

更一般地说,bash内置了printf,所以你可以像下面这样用0填充输出:

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

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

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

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

你可以做得更简单

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