在bash中,有更好的方法运行命令N次吗?

488

我偶尔会运行这样的一个bash命令行:


n=0; while [[ $n -lt 10 ]]; do some_command; n=$((n+1)); done

要连续运行some_command多次 - 在这种情况下为10次。

通常some_command是一系列命令或管道。

是否有更简洁的方法来做到这一点?


2
除了其他好的答案之外,您可以使用 let ++n 代替 n=$((n+1))(少3个字符)。 - musiphil
1
希望能提供一些关于哪些方法是可移植的指示。 - Faheem Mitha
1
http://serverfault.com/questions/273238/how-to-run-a-command-multiple-times - Ciro Santilli OurBigBook.com
9
如果您愿意更改shell,zshrepeat 10 do some_command; done。该命令可以重复执行某个命令10次。 - chepner
1
@JohnVandivier,我刚在一个新的18.04 docker容器中尝试了一下bash,在我的问题中发布的原始语法仍然有效。也许你正在运行其他类型的shell,例如/bin/sh(实际上是dash),那么就会出现sh:1:[[: not found错误提示。 - bstpierre
显示剩余3条评论
20个回答

8

首先,你可以将它封装在一个函数中:

function manytimes {
    n=0
    times=$1
    shift
    while [[ $n -lt $times ]]; do
        $@
        n=$((n+1))
    done
}

调用方式:

$ manytimes 3 echo "test" | tr 'e' 'E'
tEst
tEst
tEst

3
如果要运行的命令比没有重定向的简单命令更复杂,那么这种方法就不起作用。 - chepner
你可以让它适用于更复杂的情况,但可能需要将命令包装在函数或脚本中。 - bta
2
这里的 tr 是误导性的,它是在 manytimes 的输出上工作,而不是 echo。我认为你应该从示例中删除它。 - Dmitry Ginzburg

7

xargs和seq会有所帮助

function __run_times { seq 1 $1| { shift; xargs -i -- "$@"; } }

视图:

abon@abon:~$ __run_times 3  echo hello world
hello world
hello world
hello world

2
命令中的shift和双破折号有什么作用?'shift'真的必要吗? - sebs
2
如果命令比没有重定向的简单命令更复杂,则此方法将无法正常工作。 - chepner
非常慢,echo $RANDOM 50次需要7秒钟,即使如此每次运行它都显示相同的随机数... - BladeMight

5

我用以下循环解决了问题,其中repeat是代表循环次数的整数。

repeat=10
for n in $(seq $repeat); 
    do
        command1
        command2
    done

5

所有现有的答案似乎都需要使用 bash,并且不能在标准的BSD UNIX /bin/sh(例如OpenBSD上的ksh)中工作。

以下代码应该适用于任何BSD:

$ echo {1..4}
{1..4}
$ seq 4
sh: seq: not found
$ for i in $(jot 4); do echo e$i; done
e1
e2
e3
e4
$

5
你可以使用这个命令来重复执行你的命令10次或更多。
for i in {1..10}; do **your command**; done

例如

for i in {1..10}; do **speedtest**; done

4
另一个答案是:在空参数上使用参数扩展:
# calls curl 4 times 
curl -s -w "\n" -X GET "http:{,,,}//www.google.com"

已在Centos 7和MacOS上进行了测试。


这很有趣,你能提供更多关于为什么它有效的细节吗? - Hassan Mahmud
1
它正在运行参数扩展n次,但由于参数为空,它实际上并不改变运行的内容。 - jcollum
1
查找参数扩展和curl没有任何结果。我对curl几乎一无所知。如果你能指点我一些文章或者这个特性的另一个常用名称那就太棒了。谢谢。 - Hassan Mahmud
1
我认为这可能会有所帮助 https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html - jcollum

2
有点天真,但这通常是我脑海中记住的内容:

for i in 1 2 3; do
  some commands
done

与@joe-koberg的答案非常相似。如果您需要多次重复,他的方法更好,但对我来说更难记住其他语法,因为在过去的几年中,我没有经常使用bash,至少不是用于脚本编写。


2

使用for循环可能是正确的方法,但这里有一个有趣的替代方案:

echo -e {1..10}"\n" |xargs -n1 some_command

如果您需要迭代号作为调用参数,请使用:

echo -e {1..10}"\n" |xargs -I@ echo now I am running iteration @

编辑: 正确地评论了上面给出的解决方案只能平稳地运行简单命令(没有管道等)。 您总是可以使用sh -c来执行更复杂的操作,但不值得。

我通常使用的另一种方法是以下函数:

rep() { s=$1;shift;e=$1;shift; for x in `seq $s $e`; do c=${@//@/$x};sh -c "$c"; done;}

现在你可以这样调用它:

rep 3 10 echo iteration @

前两个数字给出范围。 @ 将被转换为迭代号。 现在你也可以将其与管道一起使用:

rep 1 10 "ls R@/|wc -l"

将会给出目录R1 .. R10中文件的数量。


1
如果命令比没有重定向的简单命令更复杂,则此方法将无法正常工作。 - chepner
很好,但请看我上面的编辑。修改后的方法使用了sh -c,因此也应该支持重定向。 - stacksia
rep 1 2 touch "foo @" 不会创建两个名为 "foo 1" 和 "foo 2" 的文件,而是会创建三个文件 "foo"、"1" 和 "2"。使用 printf%q 可能可以正确引用,但是将任意单词序列正确引用为一个字符串传递给 sh -c 将会很棘手。 - chepner
OP 要求更加简明扼要的回答,既然已经有了 5 个更简明扼要的回答,为什么还要添加这样的内容呢? - zoska
一旦您在.bashrc中定义了rep的定义,进一步的调用就变得更加简洁。 - musiphil
注意,虽然如上定义的rep将占用标准输入进行迭代,但没有充分的理由。为什么不像这样使用简单的for循环:for ((x=$s; x<=$e; ++x)); do ...; done - musiphil

2
脚本文件
bash-3.2$ cat test.sh 
#!/bin/bash

echo "The argument is  arg: $1"

for ((n=0;n<$1;n++));
do
  echo "Hi"
done

并在下方输出结果

bash-3.2$  ./test.sh 3
The argument is  arg: 3
Hi
Hi
Hi
bash-3.2$

0

5
给他一个提示并举出另一种形式的例子。 - Joe Koberg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接