GNU seq(1) 的便携式 POSIX shell 替代品?

GNU seq(1) 的便携式 POSIX shell 替代品?

我注意到seq(1)除了 GNU 系统之外,你不能真正指望在任何其他系统上都可用。seq(1)我可以用 POSIX(不是 bash)shell 编写的简单重新实现是什么?

编辑:请注意,我打算至少在各种 BSD、Solaris 和 Mac OS X 上使用它。

答案1

替代方案awkbc:

seq() (first=$1 incr=$2 last=$3
  echo "for (i = $first; i <= $last; i+=$incr) i" | bc -l
)

一个优点是您不受 CPU 双精度的大小/分辨率的限制:

$ seq '(2^200)' '(2^100)' '(2^200+2^102)'
1606938044258990275541962092341162602522202993782792835301376
1606938044258990275541962092342430253122431223184289538506752
1606938044258990275541962092343697903722659452585786241712128
1606938044258990275541962092344965554322887681987282944917504
1606938044258990275541962092346233204923115911388779648122880

但当数字太大时要注意换行:

$ seq '(2^500)' '(2^100)' '(2^500+2^101)'
32733906078961418700131896968275991522166420460430647894832913680961\
33796404674554883270092325904157150886684127560071009217256545885393\
053328527589376
32733906078961418700131896968275991522166420460430647894832913680961\
33796404674554883270092325904157150886684127560071010484907146113622\
454825230794752
32733906078961418700131896968275991522166420460430647894832913680961\
33796404674554883270092325904157150886684127560071011752557746341851\
856321934000128

答案2

根据公开组POSIX awk 支持BEGIN,因此可以通过以下方式完成awk

awk -v MYEND=6 'BEGIN { for(i=1;i<=MYEND;i++) print i }'

其中代表-v MYEND=6第一个参数中的赋值seq。换句话说,这也有效:

seq() {
    end=$1
    awk -v end=$end 'BEGIN { for( i = 1; i <= end; i++) print i }'
}

或者甚至使用三个变量开始、增量和结束。 (这不支持负增量也不支持浮动):

seq() { 
    if [ "$#" = 1 ]; then
        start=1 incr=1 end=$1
    elif [ "$#" = 2 ]; then
        start=$1 incr=1 end=$2
    elif [ "$#" = 3 ]; then
        start=$1 incr=$2 end=$3
    else 
        echo "error: invalid number of arguments" >&2
        return 1
    fi
    if ! [ "$incr" -ge 1 ]; then
        echo "error: invalid increment (must be >= 1)" >&2
        return 1
    fi
    awk -v start=$start -v incr=$incr -v end=$end '
        BEGIN {  for( i = start; i <= end; i += incr) print i }'
}

额外的索拉里斯注意:在 Solaris 上/usr/bin/awk不是符合 POSIX 标准,您需要在 Solaris 上使用nawk/usr/xpg4/bin/awk

/usr/xpg4/bin在 Solaris 上,如果您正在运行 POSIX 兼容脚本,您可能需要尽早设置PATH。

参考答案:

答案3

while是 POSIX

i=1
while [ "$i" -ne 11 ]
do    
    echo $i
    i=$(( $i + 1 ))
done

while便携式、多功能且经常未被充分利用。

答案4

这是我能想到的最短(主要目标)和最便宜(就消耗的系统资源 - 次要目标而言)的变体:

while :; do echo x; done | sed 10q

将“10”替换为所需的迭代次数。

可以在“for”循环中使用它,如下所示:

for i in `while :; do echo x; done | sed 10q`; do echo hello; done

然而,它可以在“while”循环中更有效地使用:

while :; do echo x; done | sed 10q | while read x; do echo hello; done

这避免了一次进程调用和临时存储可能大量的“x”字符作为“for”参数。

我能想到的最有效的方法是

i=10; while case $i in 0) break; esac; do i=$(($i - 1)); echo hello; done

它仅使用除“echo”之外的 shell 内置命令,并且不需要其他进程调用。不幸的是,在剧本中看到它是相当丑陋和令人不愉快的。但是,如果脚本性能至关重要,那么它是我能想到的最佳便携式解决方案。

当然,awk 和 bc 也可以完成这项工作。但两者都是比这项工作所需的功能强大得多的实用程序,因此使用它们来完成如此简单的任务对我来说似乎是对系统资源的浪费。

相关内容