有没有更好的方法可以在bash中运行N次命令?


304

我偶尔会像这样运行bash命令行:

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

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

通常some_command,实际上实际上是命令链或管道。

有更简洁的方法吗?


1
除了其他不错的答案外,您还可以使用let ++n代替n=$((n+1))(少3个字符)。
musiphil 2014年

1
这些方法中哪些可移植的一些迹象会很好。
Faheem Mitha 2015年


3
如果你愿意改变炮弹,zshrepeat 10 do some_command; done
chepner '16

1
@JohnVandivier我刚刚在一个新的18.04 docker容器中以bash进行了尝试,问题中发布的原始语法仍然有效。也许您正在运行除bash以外的其他shell,例如/ bin / sh,这确实有点破折号,在这种情况下,我会回来sh: 1: [[: not found
bstpierre

Answers:


479
for run in {1..10}
do
  command
done

或作为希望轻松复制和粘贴的人员的单线:

for run in {1..10}; do command; done

19
如果您有很多迭代,则使用的形式for (n=0;n<k;n++))可能会更好;我怀疑{1..k}会用所有用空格分隔的整数来实现一个字符串。
Joe Koberg 2010年

@Joe Koberg,感谢您的提示。我通常使用N <100,所以这看起来不错。
bstpierre 2010年

10
@bstpierre:大括号扩展形式不能使用变量(轻松地)在Bash中指定范围。
暂停,直到另行通知。

5
那是真实的。根据gnu.org/software/bash/manual/bashref.html#Brace-Expansion,在变量扩展之前执行括号扩展,因此它将永远不会看到任何变量的值。
Joe Koberg 2010年

5
关于变量扩展的可悲的事情。我正在使用以下代码来循环n次并格式化数字: n=15;for i in $(seq -f "%02g" ${n});do echo $i; done 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
user1830432

203

使用常量:

for ((n=0;n<10;n++)); do some_command; done

使用变量(可以包含数学表达式):

x=10; for ((n=0; n < (x / 2); n++)); do some_command; done

4
这是最接近编程语言^^的方法-应该被接受!
Nam G VU

这是更好的方法,因为它是单行,因此更易于在终端中使用
Kunok,

您也可以使用变量,例如x=10 for ((n=0; n < $x; n++));
Jelphy

@Kunok在同一行中运行已接受的答案也是可以接受的:for run in {1..10}; do echo "on run $run"; done
Douglas Adams

如果 n已经设置为特定值怎么办?for (( ; n<10; n++ ))不工作/编辑:最好可能使用其他答案喜欢的一个while (( n++...一个
phil294

121

破解它的另一种简单方法:

seq 20 | xargs -Iz echo "Hi there"

运行echo 20次。


注意,seq 20 | xargs -Iz echo "Hi there z"将输出:

嗨1
嗨2
...


4
您甚至不需要1(可以运行seq 20
Oleg Vaskevich 2014年

16
2015版:seq 20 | xargs -I{} echo "Hi there {}"
mitnk

4
请注意,这z不是一个好的占位符,因为它很常见,可以在命令文本中用作常规文本。使用{}会更好。
唐克

4
有什么办法丢弃该号码seq吗?这样它只会打印Hi there 20次(没有数字)?编辑:只使用-I{},然后{}在命令中没有任何内容
Mike Graf

1
只要使用某些东西,您就可以确定它不会出现在输出中,例如,IIIII然后看起来不错,例如:seq 20 | xargs -IIIIII timeout 10 yourCommandThatHangs
rubo77 '19

79

如果您使用的是zsh shell:

repeat 10 { echo 'Hello' }

其中10是重复命令的次数。


11
尽管这不是问题的答案(因为它明确提到了bash),但它非常有用,有助于我解决问题。+1
OutOfBound

2
另外,如果您只是在终端机中键入,则可以使用类似repeat 10 { !! }
Renato Gomes

28

使用GNU Parallel,您可以执行以下操作:

parallel some_command ::: {1..1000}

如果您不想将数字作为参数,而一次只运行一个作业:

parallel -j1 -N0 some_command ::: {1..1000}

观看介绍视频以获取快速介绍: https //www.youtube.com/playlist?list=PL284C9FF2488BC6D1

浏览本教程(http://www.gnu.org/software/parallel/parallel_tutorial.html)。您会用它来爱​​您。


为什么要使用一个工具,它非常存在的理由,因为显然它的名字被证明,是运行的东西并行,然后给它神秘argments -j1 -N0关闭并行 ????
Ross Presser

@RossPresser这是一个非常合理的问题。原因是使用GNU Parallel语法表达您想要做的事情可能会更容易。思考:您将如何some_command在无参数的情况下连续运行1000次?并且:您能说的比那短parallel -j1 -N0 some_command ::: {1..1000}吗?
Ole Tange

好吧,我想您有一点要说,但实际上仍然感觉像是使用精致的发动机缸体作为锤子。如果我有足够的动力,并且考虑到未来的继任者试图理解我的所作所为,那么我可能会把这些混乱包裹在一个shell脚本中,然后用来称呼它repeat.sh repeatcount some_command。为了在shell脚本中实现,我可能会使用parallel,如果它已经安装在机器上(可能不是)。或者我可能倾向于使用xargs,因为当不需要重定向时,这似乎是最快的some_command
Ross Presser

如果我的“非常合理的问题”以不合理的方式表达,我深表歉意。
Ross Presser

1
在这种特定情况下,可能不太明显。如果您使用GNU Parallel的更多选项,例如,如果您想重试失败的命令4次并将输出保存到其他文件,则变得更加明显:parallel -N0 -j1 --retries 4 --results outputdir/ some_command
Ole Tange

18

bash配置文件中的一个简单功能(~/.bashrc经常)可以很好地工作。

function runx() {
  for ((n=0;n<$1;n++))
    do ${*:2}
  done
}

这样称呼它。

$ runx 3 echo 'Hello world'
Hello world
Hello world
Hello world

1
喜欢它。现在,如果可以使用ctrl + c取消它,那就太好了
slashdottir

为什么使用这种方式回显$ RANDOM n次显示相同的数字?
BladeMight '18

3
这很完美。我需要更改的唯一一件事情是,我只是do ${*:2}添加了eval do eval ${*:2}来确保它适用于运行不是以可执行文件开头的命令,而不仅仅是这样做。例如,如果您想在运行诸如之类的命令之前设置环境变量SOME_ENV=test echo 'test'
5_nd_5

12

xargs

#!/usr/bin/bash
echo "while loop:"
n=0; time while (( n++ < 10000 )); do /usr/bin/true ; done

echo -e "\nfor loop:"
time for ((n=0;n<10000;n++)); do /usr/bin/true ; done

echo -e "\nseq,xargs:"
time seq 10000 | xargs -I{} -P1 -n1 /usr/bin/true

echo -e "\nyes,xargs:"
time yes x | head -n10000 |  xargs -I{} -P1 -n1 /usr/bin/true

echo -e "\nparallel:"
time parallel --will-cite -j1 -N0 /usr/bin/true ::: {1..10000}

在现代的64位Linux上,提供:

while loop:

real    0m2.282s
user    0m0.177s
sys     0m0.413s

for loop:

real    0m2.559s
user    0m0.393s
sys     0m0.500s

seq,xargs:

real    0m1.728s
user    0m0.013s
sys     0m0.217s

yes,xargs:

real    0m1.723s
user    0m0.013s
sys     0m0.223s

parallel:

real    0m26.271s
user    0m4.943s
sys     0m3.533s

这是有道理的,因为该xargs命令是一个单一的本机进程,可/usr/bin/true多次生成该命令,而不是在Bash中解释的forand while循环。当然,这仅适用于单个命令。如果您需要在循环的每次迭代中执行多个命令,则它与传递循环一样快,甚至更快。sh -c 'command1; command2; ...'给xargs

-P1也可以改变,比方说,-P8产卵并行8个流程,以获得速度的另一大助力。

我不知道为什么GNU并行这么慢。我以为它可以与xargs相提并论。


1
GNU Parallel做了大量的设置和簿记工作:它支持可编程的替换字符串,它缓冲输出,因此两个并行作业的输出永远不会混合,它支持方向(您不能这样做:xargs“ echo> foo”),因为它不是用bash编写,它必须产生一个新的shell来进行重定向。但是,主要原因是在Perl模块IPC :: open3中-因此,使速度更快的任何工作都会导致GNU Parallel更快。
Ole Tange'7


7

首先,您可以将其包装在一个函数中:

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

2
如果要运行的命令比没有重定向的简单命令复杂,则此方法将无效。
chepner 2013年

您可以使它适用于更复杂的情况,但是您可能必须将命令包装在函数或脚本中。
2013年

2
tr这里是误导,它的工作的输出manytimes,没有echo。我认为您应该从样本中删除它。
德米特里·金茨堡

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和双破折号起什么作用?“转变”真的必要吗?
sebs 2013年

2
如果该命令比没有重定向的简单命令复杂,则此命令将无效。
chepner 2013年

非常慢,回声$ RANDOM 50次花费了7秒钟,即使每次运行它都显示相同的随机数...
BladeMight '18


5

如果可以定期执行此操作,则可以运行以下命令无限期地每1秒运行一次。您可以放置​​其他自定义检查以运行n次。

watch -n 1 some_command

如果希望对更改进行视觉确认,请--differencesls命令前附加。

根据OSX手册页,

--cumulative选项突出显示“粘滞”,显示所有已更改位置的运行显示。-t或--no-title选项将关闭显示顶部的间隔,命令和当前时间的标题,以及以下空白行。

Linux / Unix手册页可以在这里找到


5

我用这个循环解决了,其中repeat是一个代表循环数的整数

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

4

另一个答案:对空参数使用参数扩展:

# calls curl 4 times 
curl -s -w "\n" -X GET "http:{,,,}//www.google.com"

在Centos 7和MacOS上测试。


这很有趣,您能否提供更多有关其工作原理的详细信息?
哈桑·马哈茂德

1
它正在运行参数扩展n次,但是由于该参数为空,因此实际上并没有改变运行的内容
jcollum

搜索参数扩展和卷曲没有结果。我几乎不知道卷曲。如果您可以为我指出该功能的一些文章或其他通用名称,那将非常好。谢谢。
哈桑·马哈茂德

1
我想,这可能有助于gnu.org/software/bash/manual/html_node/...
jcollum

3

所有现有的答案似乎都是require bash,并且不适用于标准的BSD UNIX /bin/sh(例如,ksh在OpenBSD上))。

以下代码可在任何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
$

2

有点天真,但这是我通常记得的东西:

for i in 1 2 3; do
  some commands
done

与@ joe-koberg的答案非常相似。特别是在您需要多次重复的情况下,他的效果更好,这对我来说更难记住其他语法,因为在过去的几年中我用bash的不是很多。我的意思是至少不要编写脚本。


1

脚本文件

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

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 2013年

足够公平,但请参阅上面的编辑。编辑的方法使用sh -c,因此也应使用重定向。
stacksia 2013年

rep 1 2 touch "foo @"不会创建两个文件“ foo 1”和“ foo 2”;而是创建三个文件“ foo”,“ 1”和“ 2”。可能有一种使用printf和来获得正确报价的方法%q,但是要正确地将一个任意的单词序列引用到单个字符串中并传递给它,将非常棘手sh -c
chepner 2013年

OP要求更简洁,那么当已经有5个更简洁的答案时,为什么还要添加这样的内容?
zoska 2013年

一旦你定义的定义rep.bashrc进一步调用变得更为简洁。
musiphil 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.