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
シェルを変更したい場合は、を使用しzshてくださいrepeat 10 do some_command; done
chepner 2016

1
@JohnVandivier bashで新しい18.04のdockerコンテナーで試したところ、質問に投稿された元の構文はまだ機能しています。たぶん、あなたは/ bin / shのようなbash以外のシェルを実行しているかもしれません。それは本当にダッシュsh: 1: [[: not foundです。その場合、私は戻ります。
bstpierre

回答:


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

10
@bstpierre:ブレース展開形式では、変数を(簡単に)使用してBashで範囲を指定することはできません。
追って通知があるまで一時停止。

5
それは本当です。ブレース展開は、gnu.org / software / bash / manual / bashref.html#Br​​ace-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これは、1つのラインで受け入れ答えを実行するだけで許容できるようだfor run in {1..10}; do echo "on run $run"; done
ダグラス・アダムス

nすでに特定の値に設定されている場合はどうなり ますか?for (( ; n<10; n++ ))doesntの仕事/編集:最高のは、おそらくのような他の回答のいずれかを使用while (( n++...1
phil294

121

それをハックする別の簡単な方法:

seq 20 | xargs -Iz echo "Hi there"

エコーを20回実行します。


seq 20 | xargs -Iz echo "Hi there z"出力されることに注意してください:

こんにちは1
こんにちは2
...


4
1も必要ありません(ただ実行できますseq 20
Oleg Vaskevich 14

16
バージョン2015:seq 20 | xargs -I{} echo "Hi there {}"
mitnk

4
zは、コマンドテキストの通常のテキストになる可能性があるため、一般的なものであるため、これは適切なプレースホルダーではないことに注意してください。使用{}が良くなります。
mitnk 2015年

4
から番号を破棄する方法はありseqますか?だからHi there 20回だけ印刷するでしょう(数字なし)?編集:コマンドで-I{}何も使用しないでください{}
Mike Graf

1
ただ、何かを使用し、あなたはそれが出力になりません確信している、例えばIIIIIそれは例えば素敵に見える:seq 20 | xargs -IIIIII timeout 10 yourCommandThatHangs
rubo77

79

zshシェルを使用している場合:

repeat 10 { echo 'Hello' }

ここで、10はコマンドが繰り返される回数です。


11
これは質問への回答ではありませんが(bashについて明示的に言及しているため)、非常に役立ち、問題の解決に役立ちました。+1
OutOfBound 2017

2
また、ターミナルでそれを入力しただけの場合、次のようなものを使用できますrepeat 10 { !! }
Renato Gomes

28

GNU Parallelを使用すると、次のことができます。

parallel some_command ::: {1..1000}

引数として数値を使用せず、一度に1つのジョブのみを実行する場合:

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

簡単な紹介については、紹介ビデオをご覧くださいhttps : //www.youtube.com/playlist?list=PL284C9FF2488BC6D1

チュートリアルをご覧くださいhttp://www.gnu.org/software/parallel/parallel_tutorial.html)。あなたはそれを愛しています。


その名前で明らかに証明されているように、物事を並列で実行し、並列処理を無効にする不可解な引数-j1 -N0を与えるためのツールを使用するのはなぜですか????
ロスプレッサー

@RossPresserそれは非常に合理的な質問です。その理由は、GNU Parallel構文を使用して、やりたいことを表現する方が簡単な場合があるからです。考えてみてください:some_command引数なしでシリアルで1000回実行するにはどうしますか?そして:それより短く表現できますparallel -j1 -N0 some_command ::: {1..1000}か?
Ole Tange

まあ、あなたは一種のポイントを持っていると思いますが、それは本当に細かく作られたエンジンブロックをハンマーとして使用するように本当に感じています。私が十分にやる気があり、自分の後継者が私が何をしたのか理解しようとしていることに思いやりがある感じたら、おそらく混乱をシェルスクリプトで囲み、でそれを呼び出すだけでしょうrepeat.sh repeatcount some_command。シェルスクリプトでの実装ではparallel、マシンに既にインストールされている場合は(おそらくインストールされないでしょう)、私はを使用する可能性があります。または、xargsに引き付けられるかもしれません。これは、xargsがリダイレクトを必要としないときに最も高速に見えるためsome_commandです。
ロスプレッサー

私の「非常に合理的な質問」が不当な方法で表現された場合は、お詫び申し上げます。
ロスプレッサー

1
この特定のケースでは、それほど明白ではない場合があります。:あなたが4回失敗したコマンドを再試行し、別のファイルに出力を保存したい場合は、GNUパラレル、例えばのより多くのオプションを使用する場合、それはより明白になったparallel -N0 -j1 --retries 4 --results outputdir/ some_command
オレ丹下

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

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コマンドはすべてBashで解釈されるand ループでは/usr/bin/trueなく、コマンドを複数回生成する単一のネイティブプロセスであるため、これは理にかなっています。もちろん、これは単一のコマンドに対してのみ機能します。ループの各反復で複数のコマンドを実行する必要がある場合は、xargsに渡すよりも速く、またはおそらく速くなります。forwhilesh -c 'command1; command2; ...'

-P1また、たとえば、に変更することができ-P8速度のもう一つの大きな後押しを得るために、並列に8つのプロセスを起動します。

なぜGNUパラレルが非常に遅いのかわかりません。私はそれがxargsに匹敵すると思いました。


1
GNU Parallelはかなり多くの設定と簿記を行います:プログラム可能な置換文字列をサポートし、出力をバッファリングするため、2つの並列ジョブからの出力が決して混合されず、方向をサポートします(実行できません:xargs "echo> foo")。 bashで書かれているため、リダイレクトを行うには新しいシェルを生成する必要があります。ただし、主な原因は、PerlモジュールIPC :: open3にあります。そのため、これより速くなるように作業すると、GNU Parallelが速くなります。
Ole


7

1つは、関数にまとめることができます。

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

より複雑なケースでも機能させることができますが、コマンドを関数またはスクリプトでラップする必要がある場合があります。
bta 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
コマンドのシフトとダブルダッシュは何をしますか?「シフト」は本当に必要ですか?
2013

2
コマンドがリダイレクトのない単純なコマンドよりも複雑な場合、これは機能しません。
チェプナー2013

非常に遅く、$ RANDOMを50回エコーすると7秒かかりましたが、実行するたびに同じ乱数が表示されます...
BladeMight


5

定期的に実行しても問題がない場合は、次のコマンドを実行して、1秒ごとに無期限に実行できます。他のカスタムチェックを配置してn回実行することができます。

watch -n 1 some_command

変更を視覚的に確認したい場合は--differenceslsコマンドの前に追加してください。

OSXのmanページによると、

--cumulativeオプションを使用すると、「スティッキー」が強調表示され、変更されたすべての位置の実行中の表示が表示されます。-tまたは--no-titleオプションは、間隔、コマンド、現在の時刻を表示するヘッダーと、次の空白行をオフにします。

Linux / Unix manページはここにあります


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でテスト済み。


これは興味深いですが、これが機能する理由についてもう少し詳しく説明していただけますか?
Hassan Mahmud

1
これはパラメーター拡張をn回実行していますが、パラメーターが空であるため、実際には何が実行されるかは変わりません
jcollum

パラメータ展開とカールを検索しても結果はありません。カールがほとんどわかりません。あなたが私にいくつかの記事またはおそらくこの機能の別の一般的な名前を示すことができればそれは素晴らしいでしょう。ありがとう。
Hassan Mahmud


3

既存の回答はすべてが必要bashであるように見え、標準のBSD UNIX /bin/shkshOpenBSDなど)では機能しません。

以下のコードはどの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 @

最初の2つの数値は範囲を示します。@反復数に変換されます。これをパイプでも使用できます:

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」の2つのファイルは作成されません。むしろ、3つのファイル「foo」、「1」、および「2」を作成します。printfand を使用して引用を正しく行う方法はあるかもしれませんが、%qに渡す単一の文字列に正しく引用された単語の任意のシーケンスを取得するのは難しいでしょうsh -c
chepner 2013

OPはより簡潔な質問をしましたが、すでに5つの簡潔な回答があるのに、なぜこのようなものを追加するのですか?
zoska 2013

での定義を定義repすると.bashrc、以降の呼び出しはより簡潔になります。
musiphil 2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.