มีวิธีที่ดีกว่าในการรันคำสั่ง N ครั้งในการทุบตี?


304

ฉันรัน bash command line เป็นครั้งคราวเช่นนี้

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

1
ข้อบ่งชี้บางอย่างของวิธีการเหล่านี้เป็นแบบพกพาจะดี
Faheem Mitha


3
ถ้าคุณยินดีที่จะเปลี่ยนเปลือกหอยมีzsh repeat 10 do some_command; done
chepner

1
@JohnVandivier ฉันเพิ่งลองใช้ทุบตีในคอนเทนเนอร์นักเทียบท่าใหม่ 18.04 ซึ่งเป็นไวยากรณ์ดั้งเดิมตามที่โพสต์ในคำถามของฉันยังใช้งานได้ บางทีคุณอาจกำลังใช้เปลือกอื่นที่ไม่ใช่ทุบตีเช่น / bin / 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

@ Joe Koberg ขอบคุณสำหรับเคล็ดลับ ฉันมักจะใช้ N <100 ดังนั้นนี่จึงดูดี
bstpierre

10
@bstpierre: แบบฟอร์มการขยายรั้งไม่สามารถใช้ตัวแปร (ง่าย) เพื่อระบุช่วงใน Bash
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

5
นั่นเป็นความจริง. การขยาย Brace จะดำเนินการก่อนการขยายตัวตัวแปรตามgnu.org/software/bash/manual/bashref.html#Brace-Expansionดังนั้นจึงไม่เคยเห็นค่าของตัวแปรใด ๆ
Joe Koberg

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
1830432

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
ดักลาสอดัมส์

เกิดอะไรขึ้นถ้า nตั้งค่าเป็นค่าเฉพาะอยู่แล้ว for (( ; n<10; n++ ))ไม่ทำงาน / แก้ไข: ที่ดีที่สุดน่าจะใช้คำตอบอย่างอื่นอย่างwhile (( n++...ใดอย่างหนึ่งเช่นเดียว
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

16
รุ่น 2015:seq 20 | xargs -I{} echo "Hi there {}"
mitnk

4
โปรดทราบว่าzไม่ใช่ตัวยึดตำแหน่งที่ดีเพราะเป็นเรื่องปกติที่อาจเป็นข้อความปกติในข้อความคำสั่ง ใช้{}จะดีกว่า
mitnk

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
ในขณะที่มันไม่ใช่คำตอบสำหรับคำถาม (เนื่องจากกล่าวถึงการทุบตีอย่างชัดเจน) แต่ก็มีประโยชน์มากและช่วยฉันในการแก้ปัญหาของฉัน +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_command1,000 ครั้งต่อเนื่องโดยไม่มีข้อโต้แย้งได้อย่างไร และ: คุณสามารถแสดงสั้นกว่านั้นได้parallel -j1 -N0 some_command ::: {1..1000}ไหม?
Ole Tange

เอาล่ะฉันคิดว่าคุณมีประเด็นอยู่บ้าง แต่ก็ยังรู้สึกเหมือนใช้เครื่องยนต์บล็อกที่ออกแบบมาอย่างประณีตเป็นค้อน ถ้าผมเป็นแรงบันดาลใจอย่างพอเพียงและrepeat.sh repeatcount some_commandรู้สึกเกรงใจผู้สืบทอดในอนาคตของฉันพยายามที่จะเข้าใจสิ่งที่ฉันได้ผมอาจจะตัดความสับสนในสคริปต์เปลือกและเพียงแค่เรียกมันว่าด้วย สำหรับการนำไปใช้ในเชลล์สคริปต์ที่ฉันอาจใช้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 config ( ~/.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

เหตุใดการใช้วิธีนี้เพื่อ echo $ RANDOM n ครั้งจึงแสดงหมายเลขเดียวกัน
BladeMight

3
มันทำงานได้อย่างสมบูรณ์แบบ สิ่งเดียวที่ฉันต้องการเปลี่ยนคือแทนที่จะทำแค่do ${*:2}เพิ่ม eval do eval ${*:2}เพื่อให้แน่ใจว่ามันจะใช้ได้กับการรันคำสั่งที่ไม่ได้ขึ้นต้นด้วย executables 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}

บน Linux 64 บิตที่ทันสมัยให้:

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คำสั่งหลาย ๆ ครั้งแทนที่จะเป็นforและwhileวนซ้ำที่ถูกตีความใน Bash แน่นอนว่านี่ใช้ได้กับคำสั่งเดียวเท่านั้น ถ้าคุณต้องทำหลายคำสั่งในการวนซ้ำแต่ละครั้งมันจะเร็วหรือเร็วกว่าการส่งsh -c 'command1; command2; ...'ไปที่ xargs

-P1ยังอาจมีการเปลี่ยนแปลงไปกล่าว-P8จะวางไข่ 8 กระบวนการในแบบคู่ขนานที่จะได้รับอีกเพิ่มขนาดใหญ่ในความเร็ว

ฉันไม่รู้ว่าทำไม GNU ขนานจึงช้า ฉันคิดว่ามันจะเปรียบได้กับ xargs


1
GNU Parallel มีการตั้งค่าและการทำบัญชีค่อนข้างมาก: มันรองรับสายการแทนที่โปรแกรมได้มันบัฟเฟอร์เอาต์พุตดังนั้นงานจากสองงานแบบขนานจะไม่ผสมกันรองรับทิศทาง (คุณไม่สามารถทำได้: xargs "echo> foo") และเนื่องจากไม่ใช่ เขียนใน bash จะต้องวางไข่เปลือกใหม่เพื่อทำการเปลี่ยนเส้นทาง อย่างไรก็ตามสาเหตุหลักอยู่ในโมดูล Perl IPC :: open3 - ดังนั้นการทำงานใด ๆ ที่ทำให้เร็วขึ้นจะส่งผลให้ GNU Parallel เร็วขึ้น
Ole Tange


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

คุณสามารถทำให้มันใช้งานได้สำหรับกรณีที่ซับซ้อนมากขึ้น แต่คุณอาจต้องห่อคำสั่งในฟังก์ชั่นหรือสคริปต์
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
กะและเส้นประสองครั้งในคำสั่งทำอะไร 'กะ' จำเป็นจริงๆหรือ?
sebs

2
สิ่งนี้จะไม่ทำงานหากคำสั่งนั้นซับซ้อนกว่าคำสั่งแบบง่ายโดยไม่มีการเปลี่ยนเส้นทาง
chepner

ช้ามาก echo $ RANDOM 50 ครั้งใช้เวลา 7 วินาทีและดังนั้นมันจึงแสดงหมายเลขสุ่มที่เหมือนกันในการรันแต่ละครั้ง
BladeMight


5

หากคุณตกลงที่จะทำมันเป็นระยะคุณสามารถเรียกใช้คำสั่งต่อไปนี้เพื่อเรียกใช้มันทุก ๆ 1 วินาทีอย่างไม่มีกำหนด คุณสามารถใส่เช็คแบบกำหนดเองอื่น ๆ เพื่อรันเป็นจำนวนครั้ง

watch -n 1 some_command

หากคุณต้องการได้รับการยืนยันการเปลี่ยนแปลงภาพผนวก--differencesก่อนlsคำสั่ง

ตามหน้า man OSX ยังมี

ตัวเลือก - การสะสมทำให้การเน้น "เหนียว" นำเสนอการแสดงผลการทำงานของทุกตำแหน่งที่เคยมีการเปลี่ยนแปลง อ็อพชัน -t or - no-title ปิดส่วนหัวที่แสดงช่วงเวลาคำสั่งและเวลาปัจจุบันที่ด้านบนของจอแสดงผลรวมถึงบรรทัดว่างต่อไปนี้

พบหน้า man Linux / Unix ได้ที่นี่


5

ฉันแก้ไขด้วยลูปนี้โดยที่การทำซ้ำเป็นจำนวนเต็มที่แทนจำนวนลูป

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

การค้นหาการขยายพารามิเตอร์และ curl ทำให้ไม่มีผลลัพธ์ ฉันเพิ่งรู้ม้วน มันจะยอดเยี่ยมถ้าคุณสามารถชี้ให้ฉันดูบทความหรือชื่อสามัญอื่นสำหรับคุณสมบัตินี้ ขอบคุณ
Hassan Mahmud

1
ผมคิดว่านี่อาจช่วยgnu.org/software/bash/manual/html_node/...
jcollum

3

คำตอบที่มีอยู่ทั้งหมดดูเหมือนจะต้องการ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

สำหรับลูปน่าจะเป็นวิธีที่ถูกต้องที่จะทำ แต่นี่เป็นทางเลือกที่สนุก:

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

หากคุณต้องการหมายเลขซ้ำเป็นพารามิเตอร์สำหรับการเรียกใช้ของคุณให้ใช้:

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

แก้ไข:มีการให้ความเห็นอย่างถูกต้องว่าโซลูชันที่ระบุด้านบนจะทำงานได้อย่างราบรื่นเฉพาะเมื่อมีการเรียกใช้คำสั่งง่าย ๆ (ไม่มีไพพ์ ฯลฯ ) คุณสามารถใช้ a 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

เมื่อคุณนิยามคำจำกัดความrepใน.bashrcคำว่าการร้องขอเพิ่มเติมจะกลายเป็นเรื่องที่กระชับมากขึ้น
musiphil
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.