如何使 mksh for 循环从 1 到 N

如何使 mksh for 循环从 1 到 N

所以我尝试这样做

for x in {1..7000}
do
   echo $x
done

输出是{1..7000}

我还了解到 C 风格的 for 循环在 mksh 中不起作用

我做了一些谷歌搜索,但主要是关于 ksh 的信息,而不是 mksh

那么在 mksh 中迭代范围的正确方法是什么?

笔记:我对创建迭代和类似 C 的行为的正确方法更感兴趣,而不是打印字符串。

答案1

所以,首先,按原样这里简要讨论常见的形式...

for arg in {brace..expanded..set}; do ...

...在我看来,这是 shell 编程错误的典范。它在循环中生成一个公式集,将每个结果存储在一个分隔数组中,然后传递给另一个循环将数组表示为要迭代的列表。

作为一般规则,括号扩展在编程环境中并不是特别有用,因为所有 shell 编程都以某种方式宏观的和大括号扩展已经是一个宏。虽然大括号扩展是(我也认为)A很有用交互式 shell 功能正是因为它是一个宏,否则可能会造成浪费。

支撑扩展也不是便携式的(阅读:POSIX-便携式)。据我所知,您通常可以依赖它在zshbashyash、的当前版本中工作,但在其他地方则不行。ksh93即使在这些 shell 中,也可以使用 shell 选项禁用它,因此在 shell 函数上下文中不能绝对依赖它,尽管通常可以在$-或 的输出中测试它的可用性set +o。在它们之间,上述每个 shell 都需要不同类型的测试,因此如果您计划在函数中使用它并想要对其进行测试,请检查目标 shell 的手册以了解具体信息。

在其他 shell 中,您可以通过拆分命令替换的输出来获得几乎相同的行为 - 除了必须调用外部可执行文件来生成该集合之外$IFS。在该seq程序可用的情况下,这很容易被证明,例如......

unset IFS       
### ^ensure $IFS default behavior
for arg in $(seq 7000)
do  printf %d\\n "$arg"
done

...或者...

unset IFS
printf %d\\n $(seq 7000)

...但是,正如所写,除了缺点之外,两者都没有提供任何其他内容...

seq 7000

尽管如此,对于第一个示例,该方法与大括号扩展版本几乎相同。在这两种情况下,其他一些函数或进程都会生成一个完整的参数列表,for然后控制构造会对其进行迭代。但在后一种情况下,(对于除 之外的外壳ksh93还有管道打开/拆卸、分叉 shell 进程及其子进程seq以及最后在$IFS.

$IFS顺便说一句,根据所使用的外壳,分裂发生的速度非常不同。bash例如,在简单的$IFS分割性能方面通常很糟糕,而dash- 取决于分割字符串的行长度 - 通常可以像其他 shell 进行大括号扩展一样快。

不过,无论您使用大括号扩展还是$IFS拆分,最终结果都是您将运行一些循环来生成公式列表,然后运行另一个循环来迭代该列表。相反,您可能 - 并且可能应该 - 做的是对生成列表的公式的应用程序运行一个循环,尽快处理使用过的迭代器,并测试每次迭代的结果令人满意的结果。你会在 C 中做这样的事情......

for ( x = 0; x < 10; x++ ){ ...

这实际上也与您可以在bc.它非常像方便的语法形式,可以在ksh93,zshbash类似中使用:

for (( x=0; x<10; x++ )); do ...

然而,这也不是便携式的(再次阅读:POSIX)。相反,您可以便携地执行以下操作:

iterator=$(( start_value - interval ))
while [ "$(( iterator += interval ))" -le "$end_value" ]; do ...

...或者...

iterator=$(( start_value - interval ))
while [ "$(( ( iterator += interval ) <= end_value ))" -ne 0 ]; do ...

POSIX 指定类似于 shell 算术扩展和 C 语言算术运算的功能奇偶校验(即涉及整数的情况)。因此,您通常可以方便且可移植地迭代复杂的公式,只需一次扩展 - 甚至可以同时同步迭代多个变量。

另一方面,这样的构造可能会让您很难对抗另一种 C 语言与 shell 语言的不平等。 Shell 读取和写入几乎总是字面上的read()s 和s - 每个系统调用本身 - 并且在 shell 语法中write()没有可移植的fread(),fwrite()和/或类似物。setbuf()因此,任何在每次迭代中执行任何此类操作的循环(即使仅使用内置函数执行此操作)都必然会导致性能下降。

不过,有时您可以在 shell 脚本中串行或并行生成和运行整个集合之间找到一个折衷方案。

set 0 1 2 3 4 5 6 7 8 9
for n do printf "$n%d\n" "$@"; done

write()在十次迭代中执行了十次,并打印了从 00 - 99 的换行划分序列。这是一个很小的例子 - 而且仍然很浪费,因为 30 字节write()与 3 字节相比很难有显着改进,write()但它是这种类型的证明您可能使用的方法。正如我之前提到的,shell 编程语言非常有用宏观的在那里面一切都是字符串。因此,如果您可以将目标集分解为更小、更易于管理的集合,然后在循环上下文中重复调用它们以最终生成目标集,您通常可以获得性能更高的结果。

这是一个更复杂的、类似宏的示例:

sh -c '
    i=0 _i=-25
    set "$1" "$1" "$1" "$1" "$1"
    for n do eval "
        printf %b%d $@ $@ $@ $@ $@"
done' -- \
    '"\01$((1+(i<(i+=!(_i=(_i+=25)%275)))))" "$i$_i"'

write()对于平均每个约 100 个字节的内容,它会迭代 5 次并打印:

10      125     150     175     1100    1125    1150    1175    1200    1225    1250
20      225     250     275     2100    2125    2150    2175    2200    2225    2250
30      325     350     375     3100    3125    3150    3175    3200    3225    3250
40      425     450     475     4100    4125    4150    4175    4200    4225    4250
50      525     550     575     5100    5125    5150    5175    5200    5225    5250
60      625     650     675     6100    6125    6150    6175    6200    6225    6250
70      725     750     775     7100    7125    7150    7175    7200    7225    7250
80      825     850     875     8100    8125    8150    8175    8200    8225    8250
90      925     950     975     9100    9125    9150    9175    9200    9225    9250
100     1025    1050    1075    10100   10125   10150   10175   10200   10225   10250
110     1125    1150    1175    11100   11125   11150   11175   11200   11225   11250
120     1225    1250    1275

相关内容