bash计数脚本,按升序计数很好,按降序计数出错

bash计数脚本,按升序计数很好,按降序计数出错

以下 bash 脚本适用于 1..1000 的高计数范围

由于计数范围较高,大于 1..1000000,因此需要一些时间才能启动。总的来说,它运行良好。

for i in {1..10}; do
    printf '\r%2d' $i
    sleep 1
done
printf '\n'

对于从 99..1 开始的数字范围倒数,它工作得很好。

对于从高于 99 开始的数字范围进行倒计时,pe 100..1 输出会打印过多带有零的数字。你怎样才能避免这种情况呢?

答案1

您的问题:

  1. 该命令需要一些时间才能启动。这是由于您对循环使用了大括号扩展。扩展必须首先扩大,这意味着 shell 必须创建一个列表,其中包含要循环的范围内的每个数字。这需要时间(创建列表)和内存(存储列表)。在我的机器上,我可以看到bash进程增长从大约 1400 KiB 到 256 MiB当我要求它创建列表时{1..1000000}

    相反,考虑使用算术循环,

    for (( i=1000000; i >= 1; --i )); do ...; done
    

    或 POSIX 兼容循环,

    i=1000000
    while [ "$i" -ge 1 ]; do ...; i=$(( i - 1 )); done
    

    这两种方法都不会在静态数字列表上循环,而是根据 测试值$i1如果测试成功则运行循环体,然后i在每次迭代中递减变量 的值。

  2. 每个数字的末尾都会有“额外的零”。这是因为您通过输出回车符将光标移回行首。这会移动光标,但不会清除该行,因此如果新数字的位数较少,则最后输出的数字的末尾仍然可见。

    要解决这个问题,您可以尝试使用VT100 转义代码\e[2K用于在将光标移动到行首之前清除整行 ( ):

    for (( i=1000000; i >= 1; --i )); do
        printf '\e[2K\r%d' "$i"
        sleep 1
    done
    printf '\n'
    

    \e[1K也将起作用,因为它会清除光标左侧的行区域。或者使用\r%d\e[0K清除线路正确的输出数字后光标的位置。

答案2

您看到的问题是,您首先写入100,然后99最后1从同一位置开始,但您在输出格式中仅指定了两位数字%2d

您尚未提供任何擦除指令,因此这就是您所看到的内容 - 但全部都在一行上

What       # What
you        # was
see        # output


100        # 100
990        # 99
980        # 98
...
 90        # 9
 80        # 8
...
 10        # 1

您应该使用三位数字进行格式化或添加空格后缀:

printf "\r%3d" $i     # One option
printf "\r%2d " $i    # Another option

答案3

如果您想用另一个字符串替换一个字符串,则需要两个字符串具有相同的大小(或更长的非打印字符(空格?))。

如果将已打印的内容替换1237,则会得到723或 ,127具体取决于您是在开头还是在结尾打印。

要做到这一点(相同大小的字符串),您可以执行以下任一操作:

  • 使用稳定大小的大括号扩展:{000..123}
  • 使用一个 printf 来填充整个空间:printf '\r%5s' "$i"
  • 打印一长串空格:printf '\r%s ' "$i"
  • 清除该行直至其末尾:printf '\r%s\e[K' "$i"
  • 清除整行然后打印值:printf '\e[2K\r%s' "$i"

但使用 {1..1000000} 并不是最好的解决方案,因为它会生成一长串数字,需要将其保存在内存中,然后按空格进行划分,然后在循环中使用。 bash 中更好的解决方案是:

for(( i=1; i<1000; i++ )); do
    printf '\r%5d' $i
    sleep 0.01
done
echo

相关内容