Lặp lại lệnh Unix cứ sau x giây


481

Có một lệnh Unix tích hợp repeatcó đối số đầu tiên là số lần lặp lại lệnh, trong đó lệnh (với bất kỳ đối số nào) được chỉ định bởi các đối số còn lại đểrepeat .

Ví dụ,

% repeat 100 echo "I will not automate this punishment."

sẽ lặp lại chuỗi đã cho 100 lần rồi dừng lại.

Tôi muốn một lệnh tương tự - hãy gọi nó forever- hoạt động tương tự ngoại trừ đối số đầu tiên là số giây để tạm dừng giữa các lần lặp lại và nó lặp lại mãi mãi. Ví dụ,

% forever 5 echo "This will get echoed every 5 seconds forever and ever."

Tôi nghĩ tôi sẽ hỏi nếu một thứ như vậy tồn tại trước khi tôi viết nó. Tôi biết nó giống như một tập lệnh Perl hoặc Python 2 dòng, nhưng có lẽ có một cách chuẩn hơn để làm điều này. Nếu không, vui lòng đăng một giải pháp bằng ngôn ngữ kịch bản yêu thích của bạn, Rosetta Stone phong cách .

Tái bút: Có lẽ cách tốt hơn để làm điều này là khái quát hóa repeatđể lấy cả số lần lặp lại (với -1 nghĩa là vô cùng) và số giây để ngủ giữa các lần lặp lại. Các ví dụ trên sẽ trở thành:

% repeat 100 0 echo "I will not automate this punishment."
% repeat -1 5 echo "This will get echoed every 5 seconds forever."

1
Tại sao không: lặp lại lệnh [-t time] [-n number] [args ...]?
Jonathan Leffler

17
Mát mẻ. Thật không may cả hai trên Ubuntu của tôi và AIX tôi lặp lạikhông tìm thấy lệnh . Bạn có thể làm một type repeatvà cho tôi biết nơi đến từ đâu?

3
Tôi cũng đang dùng Ubuntu và không cài đặt lại. Bất kỳ thông tin về cách cài đặt này sẽ hữu ích.
Rory

7
repeatlà một lệnh dựng sẵn trong csh và tcsh.
Keith Thompson

2
@KeithThndry, csh, tcsh và zsh . Mặc dù đó là một từ khóa nhiều hơn một nội dung
Stéphane Chazelas

Câu trả lời:


616

Hãy thử watchlệnh.

Usage: watch [-dhntv] [--differences[=cumulative]] [--help] [--interval=<n>] 
             [--no-title] [--version] <command>`

Vậy nên:

watch -n1  command

sẽ chạy lệnh mỗi giây (tốt, về mặt kỹ thuật, cứ sau một giây cộng với thời gian cần thiết commandđể chạy watch(ít nhất là procpsbusyboxthực hiện) chỉ cần ngủ một giây ở giữa hai lần chạy command), mãi mãi.

Bạn có muốn truyền lệnh cho execthay vì sh -csử dụng -xtùy chọn:

watch -n1 -x command

Trên macOS, bạn có thể nhận được watchtừ Cổng Mac :

port install watch

Hoặc bạn có thể lấy nó từ Homebrew :

brew install watch

2
bạn có thể sử dụng MacPorts để cài đặt nó. đồng hồ cài đặt cổng sudo

10
Cũng có sẵn trong homebrew (đồng hồ cài đặt bia).
Lyle

28
+1 Đối với một công cụ mạnh mẽ mới. Tôi đã từng while true; do X; sleep Y; done- Đây là cách tốt hơn.
Adam Matan

4
Vấn đề với công cụ này (ít nhất là trên Ubuntu) là nó buộc toàn màn hình, vì vậy nếu tôi bỏ một số thông tin định kỳ, tôi sẽ mất thông tin trước đó.
scorpiodawg

4
Nó được gọi cmdwatchtrên FreeBSD (từ các cổng sysutils/cmdwatch:).
Ouki

243

Bash

while+ sleep:

while true
do 
    echo "Hi"
    sleep 1
done

Đây là điều tương tự như một cách viết tắt (Từ các bình luận bên dưới):

while sleep 1; do echo "Hi"; done

Sử dụng ;để phân tách các lệnh và sử dụng sleep 1cho whilekiểm tra vì nó luôn trả về đúng. Bạn có thể đặt nhiều lệnh hơn trong vòng lặp - chỉ cần tách chúng với;


2
Tôi tin rằng nó cũng hoạt động như được viết trong một vỏ Bourne chung.
dmckee

5
@dreeves, sử dụng ";" là dấu phân cách, các lệnh có thể được thực thi trong một dòng duy nhất. Hãy xem câu trả lời của Toony làm ví dụ
bbaja42

56
ngủ 1 luôn trả về true để bạn có thể sử dụng nó như điều kiện while. Không phải là thứ dễ đọc nhất trên thế giới nhưng hoàn toàn hợp pháp: trong khi ngủ 1; làm tiếng vang "Hi"; xong
David Costa

3
@David Costa: Bạn đã đưa ra quan điểm hợp lý, nhưng sleepkhông phải lúc nào cũng trở lại đúng. Đã sử dụng các vòng lặp như vậy (mặc dù có độ trễ dài hơn) cụ thể là vì có một cách để chấm dứt nó một cách duyên dáng với việc giết chết quá trình ngủ.
Incni Mrsi

2
@IncnisMrsi Tôi không nghĩ về nó, nó thực sự chỉ trả về 0 khi thời gian trôi qua và khác không khi nó bị chấm dứt bởi tín hiệu. Cảm ơn bạn đã chỉ ra
David Costa

71

Đây chỉ là phiên bản ngắn hơn của các while+sleepcâu trả lời khác , nếu bạn đang chạy loại nhiệm vụ này thường xuyên như thói quen hàng ngày của mình, thì việc này sẽ giúp bạn tránh được các lần nhấn phím không cần thiết và nếu dòng lệnh của bạn bắt đầu hiểu lâu hơn thì việc này sẽ dễ dàng hơn một chút. Nhưng cái này bắt đầu bằng việc ngủ trước.

Điều này thường hữu ích nếu bạn cần theo dõi thứ gì đó có đầu ra một dòng như tải máy:

while sleep 1; do uptime; done

Đúng. Điều này nên được ghi lại bên cạnh UUOC.
Johan

10
Và nếu bạn muốn nó hoạt động như "xem" bạn có thể làmwhile clear; do date; command;sleep 5; done
Johan

Wow, cảm ơn vì while sleepsự kết hợp, tiết kiệm keystokes!
George Y.

45

Một vấn đề mà tất cả các câu trả lời được đăng cho đến nay là thời gian thực hiện lệnh có thể trôi. Ví dụ: nếu bạn làm mộtsleep 10 lệnh giữa và lệnh mất 2 giây để chạy, thì nó sẽ chạy cứ sau 12 giây; nếu phải mất một lượng thời gian khác nhau để chạy, thì về lâu dài thời gian khi nó chạy có thể không dự đoán được.

Đây có thể chỉ là những gì bạn muốn; nếu vậy, hãy sử dụng một trong các giải pháp khác hoặc sử dụng giải pháp này nhưng đơn giản hóa sleepcuộc gọi.

Đối với độ phân giải một phút, các công việc cron sẽ chạy tại thời điểm được chỉ định, bất kể mỗi lệnh mất bao lâu. (Trên thực tế, một công việc định kỳ mới sẽ khởi chạy ngay cả khi công việc trước đó vẫn đang chạy.)

Đây là một tập lệnh Perl đơn giản ngủ cho đến khoảng thời gian tiếp theo, vì vậy, ví dụ với khoảng thời gian 10 giây, lệnh có thể chạy vào lúc 12:34:00, 12:34:10, 12:34:20, v.v., ngay cả khi lệnh tự mất vài giây. Nếu lệnh chạy nhiều hơn intervalgiây, khoảng thời gian tiếp theo sẽ bị bỏ qua (không giống như cron). Khoảng thời gian được tính tương đối với kỷ nguyên, do đó, khoảng thời gian 86400 giây (1 ngày) sẽ chạy vào nửa đêm UTC.

#!/usr/bin/perl

use strict;
use warnings;

if (scalar @ARGV < 2) {
    die "Usage: $0 seconds command [args...]\n";
}

$| = 1;  # Ensure output appears

my($interval, @command) = @ARGV;

# print ">>> interval=$interval command=(@command)\n";

while (1) {
    print "sleep ", $interval - time % $interval, "\n";
    sleep $interval - time % $interval;
    system @command; # TODO: Handle errors (how?)
}


1
đơn giản hơn trong bash để giảm thiểu sự trôi dạt (mặc dù nó có thể trôi qua các cửa sổ sử dụng rất dài do lệnh ngủ và bash tự tích lũy thời gian thực hiện nhỏ của nó): while :; do sleep 1m & command; wait; done
toán

1
@math: Khéo léo. Nó không hoạt động nếu bạn có các quá trình nền khác đang chạy trong trình bao hiện tại ( waitsẽ đợi tất cả chúng). Đó là fixable bằng cách thay đổi waitđể wait $!, nơi $!là PID của sleeptiến trình. Gửi bài này như một câu trả lời và tôi sẽ nâng cao nó. Nhưng nó tạo ra một số đầu ra không liên quan trong một vỏ tương tác.
Keith Thompson

29

Tôi nghĩ rằng tất cả các câu trả lời ở đây cho đến nay đều quá phức tạp, hoặc thay vào đó trả lời một câu hỏi khác:

  • "Cách chạy chương trình nhiều lần để có độ trễ X giây giữa khi chương trình kết thúc và chương trình tiếp theo bắt đầu" .

Câu hỏi thực sự là:

  • "Cách chạy chương trình cứ sau X giây"

Đây là hai điều rất khác nhau khi lệnh mất thời gian để hoàn thành.

Ví dụ như tập lệnh foo.sh(giả vờ đây là một chương trình mất vài giây để hoàn thành).

#!/bin/bash
# foo.sh
echo `date +"%H:%M:%S"` >> output.txt;
sleep 2.5;
# ---

Bạn muốn chạy cái này mỗi giây, và hầu hết sẽ gợi ý watch -n1 ./foo.sh, hoặc while sleep 1; do ./foo.sh; done. Tuy nhiên, điều này mang lại đầu ra:

15:21:20
15:21:23
15:21:27
15:21:30
15:21:34

Mà chính xác là không được chạy mỗi giây. Ngay cả với -pcờ, như man watchtrang gợi ý có thể giải quyết điều này, kết quả vẫn như vậy.

Một cách dễ dàng để thực hiện tác vụ mong muốn, mà một số chạm vào, là chạy lệnh trong nền. Nói cách khác:

while sleep 1; do (./foo.sh &) ; done

và đấy là tất cả của nó.

Bạn có thể chạy nó cứ sau 500 ms sleep 0.5, hoặc những gì có bạn.


1
Những người đã bỏ phiếu cho các câu trả lời khác nhau, không hiểu được sự tao nhã trong câu trả lời của bạn ...
Serge Stroobandt

Điều này là tốt trừ khi chương trình của bạn có tác dụng phụ. Nếu chương trình mất nhiều thời gian hơn khoảng thời gian, các tác dụng phụ có thể gây trở ngại (như ghi đè một tệp). Nếu chương trình chờ đợi một thứ khác và thứ khác sẽ mất mãi mãi, thì bạn sẽ bắt đầu ngày càng nhiều công việc nền tảng vô thời hạn. Sử dụng cẩn thận.
John Moeller

4
có thể đảo ngược thứ tự nền để đảm bảo rằng không nhiều hơn một ./foo.sh được chạy cùng một lúc, nhưng không nhanh hơn 1 mỗi giây: while :; do sleep 1 & ./foo.sh; wait; done
toán

Vâng, nó sẽ trôi, một vài phần nghìn giây trên mỗi vòng lặp, nhưng nó sẽ. Thực hiện date +"%H:%M:%S.%N"trong foo.sh để xem phân số sẽ tăng chậm như thế nào trên mỗi vòng lặp.
Isaac

Vâng, đúng là hầu hết các câu trả lời không thực sự giải quyết câu hỏi. Nhưng đó là một vụ cá cược gần như an toàn mà họ trả lời những gì OP muốn. Sẽ an toàn hơn nếu OP thực sự chấp nhận câu trả lời ... Nhưng điều này tốt, miễn là bạn nhận thức được các tác dụng phụ có thể xảy ra bạn chắc chắn rằng thời gian chạy trung bình của lệnh sẽ không vượt quá thời gian chu kỳ
Auspex

17

Trong bash:

bash -c 'while [ 0 ]; do echo "I will not automate this punishment in absurdum."; done'

( echocó thể được thay thế bằng bất kỳ lệnh nào ...

Hoặc trong perl:

perl -e 'for (;1;) {print "I will not automate this punishment in absurdum.\n"}'

Trường hợp print "I will not automate this punishment in absurdum.\n"có thể được thay thế bằng lệnh "bất kỳ" được bao quanh bằng backticks (`).

Và để tạm dừng, hãy thêm một sleepcâu lệnh bên trong vòng lặp for:

bash -c 'while [ 0 ]; do echo "I will not automate this punishment in absurdum."; sleep 1; done'

perl -e 'for (;1;) {print "I will not automate this punishment in absurdum.\n"; sleep 1}'

16
while [ 0 ]là một cách xấu để viết nó. Nó có nghĩa 0là một chuỗi có độ dài> 0. while [ 1 ]hoặc while [ jeff ]sẽ làm điều tương tự. Tốt hơn để viết while true.
Mikel

5
@Mikel: Hoặcwhile :
Keith Thompson

10

Bash gần đây> = 4.2 trong nhân Linux gần đây, dựa trên câu trả lời.

Để giới hạn thời gian thực hiện, không có dĩa ! Chỉ tích hợp được sử dụng.

Đối với điều này, tôi sử dụng readchức năng dựng sẵn thay vì sleep. Unfortunely này sẽ không làm việc với notty phiên.

Hàm nhanh " repeat" theo yêu cầu:

repeat () {
   local repeat_times=$1 repeat_delay=$2 repeat_foo repeat_sleep
   read -t .0001 repeat_foo
   if [ $? = 1 ] ;then
       repeat_sleep() { sleep $1 ;}
   else
       repeat_sleep() { read -t $1 repeat_foo; }
   fi
   shift 2
   while ((repeat_times)); do
        ((repeat_times=repeat_times>0?repeat_times-1:repeat_times))
        "${@}"
        ((repeat_times))&& ((10#${repeat_delay//.})) &&
            repeat_sleep $repeat_delay
   done
}

Kiểm tra nhỏ với chuỗi trích dẫn:

repeat 3 0 printf "Now: %(%T)T, Hello %s.\n" -1 Guy
Now: 15:13:43, Hello Guy.
Now: 15:13:43, Hello Guy.
Now: 15:13:43, Hello Guy.

repeat -1 .5 printf "Now: %(%T)T, Hello %s.\n" -1 Guy
Now: 15:14:14, Hello Guy.
Now: 15:14:14, Hello Guy.
Now: 15:14:15, Hello Guy.
Now: 15:14:15, Hello Guy.
Now: 15:14:16, Hello Guy.
Now: 15:14:16, Hello Guy.
Now: 15:14:17, Hello Guy.
Now: 15:14:17, Hello Guy.
Now: 15:14:18, Hello Guy.
Now: 15:14:18, Hello Guy.
^C

Tùy thuộc vào mức độ chi tiết và thời gian của lệnh được gửi ...

Theo các nhân Linux gần đây, có một gói /proc/timer_listchứa thông tin thời gian tính bằng nano giây.

Nếu bạn muốn chạy một lệnh chính xác từng giây một, lệnh của bạn phải kết thúc sau chưa đầy một giây! từ đó, bạn sleepchỉ còn lại phần thứ hai hiện tại.

Nếu độ trễ là quan trọng hơn và lệnh của bạn không yêu cầu thời gian đáng kể, bạn có thể:

command=(echo 'Hello world.')
delay=10
while :;do
    printf -v now "%(%s)T" -1
    read -t $(( delay-(now%delay) )) foo
    ${command[@]}
  done.

Nhưng nếu mục tiêu của bạn là đạt được độ chi tiết tốt hơn, bạn phải:

Sử dụng thông tin nano giây để đợi cho đến khi bắt đầu một giây ...

Đối với điều này, tôi đã viết một hàm bash nhỏ:

# bash source file for nano wait-until-next-second

mapfile  </proc/timer_list _timer_list
for ((_i=0;_i<${#_timer_list[@]};_i++));do
    ((_c+=${#_timer_list[_i]}))
    [[ ${_timer_list[_i]} =~ ^now ]] && TIMER_LIST_READ=$_c
    [[ ${_timer_list[_i]} =~ offset:.*[1-9] ]] && \
        TIMER_LIST_OFFSET=${_timer_list[_i]//[a-z.: ]} && \
        break
done
unset _i _timer_list _c
readonly TIMER_LIST_OFFSET TIMER_LIST_READ
waitNextSecondHires() {
    local nsnow nsslp
    read -N$TIMER_LIST_READ nsnow </proc/timer_list
    nsnow=${nsnow%% nsecs*}
    nsnow=$((${nsnow##* }+TIMER_LIST_OFFSET))
    nsslp=$((2000000000-10#${nsnow:${#nsnow}-9}))
    read -t .${nsslp:1} foo
}

Sau khi tìm nguồn cung ứng chúng, bạn có thể:

command=(echo 'Hello world.')
while :;do
    waitNextSecondHires
    ${command[@]}
  done.

chạy ${command[@]}trực tiếp trên dòng lệnh, so với

command=(eval "echo 'Hello world.';sleep .3")
while :;do
    waitNextSecondHires
    ${command[@]}
  done.

điều này phải cho kết quả chính xác như nhau.

Thuê chức năng " repeat" theo yêu cầu:

Bạn có thể nguồn này:

mapfile  </proc/timer_list _timer_list
for ((_i=0;_i<${#_timer_list[@]};_i++));do
    ((_c+=${#_timer_list[_i]}))
    [[ ${_timer_list[_i]} =~ ^now ]] && TIMER_LIST_READ=$_c
    [[ ${_timer_list[_i]} =~ offset:.*[1-9] ]] && \
        TIMER_LIST_OFFSET=${_timer_list[_i]//[a-z.: ]} && \
        break
done
unset _i _timer_list _c
readonly TIMER_LIST_OFFSET TIMER_LIST_READ

repeat_hires () {
    local repeat_times=$1 repeat_delay=$2 repeat_foo repeat_sleep repeat_count
    read -t .0001 repeat_foo
    if [ $? = 1 ] ;then
        repeat_sleep() { sleep $1 ;}
    else
        repeat_sleep() { read -t $1 repeat_foo; }
    fi
    shift 2
    printf -v repeat_delay "%.9f" $repeat_delay
    repeat_delay=${repeat_delay//.}
    read -N$TIMER_LIST_READ nsnow </proc/timer_list
    nsnow=${nsnow%% nsec*}
    started=${nsnow##* }
    while ((repeat_times)); do
        ((repeat_times=repeat_times>0?repeat_times-1:repeat_times))
        "${@}"
        ((repeat_times)) && ((10#$repeat_delay)) && {
            read -N$TIMER_LIST_READ nsnow </proc/timer_list
            nsnow=${nsnow%% nsec*}
            nsnow=${nsnow##* }
            (( (nsnow - started) / 10#$repeat_delay - repeat_count++ )) &&
                printf >&2 "WARNING: Command '%s' too long for %f delay.\n" \
                           "${*}" ${repeat_delay:0:${#repeat_delay}-9
                           }.${repeat_delay:${#repeat_delay}-9}
            printf -v sleep "%010d" $((
                10#$repeat_delay - ( ( nsnow - started ) % 10#$repeat_delay ) ))
            repeat_sleep ${sleep:0:${#sleep}-9}.${sleep:${#sleep}-9}
        }
    done
}

Sau đó thử nó:

time repeat_hires 21 .05 sh -c 'date +%s.%N;sleep .01'
1480867565.152022457
1480867565.201249108
1480867565.251333284
1480867565.301224905
1480867565.351236725
1480867565.400930482
1480867565.451207075
1480867565.501212329
1480867565.550927738
1480867565.601199721
1480867565.651500618
1480867565.700889792
1480867565.750963074
1480867565.800987954
1480867565.853671458
1480867565.901232296
1480867565.951171898
1480867566.000917199
1480867566.050942638
1480867566.101171249
1480867566.150913407

real    0m1.013s
user    0m0.000s
sys     0m0.016s

time repeat_hires 3 .05 sh -c 'date +%s.%N;sleep .05'
1480867635.380561067
WARNING: Command 'sh -c date +%s.%N;sleep .05' too long for 0.050000 delay.
1480867635.486503367
WARNING: Command 'sh -c date +%s.%N;sleep .05' too long for 0.050000 delay.
1480867635.582332617

real    0m0.257s
user    0m0.000s
sys     0m0.004s

read -tlà một tích hợp , trong khi sleepkhông. Điều này có thể có hiệu ứng viền (tức là nhấn [key: return] làm gián đoạn giấc ngủ ), nhưng điều này có thể được xử lý bằng thử nghiệm$foo
F. Hauri

Câu trả lời rất ấn tượng
gena2x

4

Muốn thử cái này không (Bash)?

forever ()   {
    TIMES=shift;
    SLEEP=shift;
    if [ "$TIMES" = "-1" ]; then  
        while true;
        do 
            $@
            sleep $SLEEP
        done
    else
        repeat "$TIMES" $@ 
    fi; }

Tôi không nóng ở vỏ, vì vậy cải tiến hoan nghênh. Và tôi dường như không có lệnh "lặp lại" trong bản phân phối của mình.

2
repeatlà cụ thể cho csh và tcsh.
Keith Thompson

2
@KeithThndry: và zsh
Thor

4

Perl

#!/usr/bin/env perl
# First argument is number of seconds to sleep between repeats, remaining
# arguments give the command to repeat forever.

$sleep = shift;
$cmd = join(' ', @ARGV);

while(1) {
  system($cmd);
  sleep($sleep); 
}

4

Bash và một số loại vỏ của nó có sự tiện lợi (( ... )) ký hiệu trong đó các biểu thức số học có thể được đánh giá.

Vì vậy, như một câu trả lời cho thử thách thứ ba của bạn, trong đó cả số lần lặp lại và độ trễ giữa mỗi lần lặp lại đều có thể định cấu hình được, đây là một cách để thực hiện:

repeat=10
delay=1

i=0
while (( i++ < repeat )); do
  echo Repetition $i
  sleep $delay
done

Câu trả lời này cũng bị trôi dạt theo thời gian trong câu trả lời của Keith .


Đây là một trong những tốt nhất.
Ahmad Awais

3

Như được đề cập bởi gbrandt, nếu watch lệnh có sẵn, chắc chắn sử dụng nó. Tuy nhiên, một số hệ thống Unix không được cài đặt theo mặc định (ít nhất là chúng không hoạt động ở nơi tôi làm việc).

Đây là một giải pháp khác với cú pháp và đầu ra hơi khác nhau (hoạt động trong BASH và SH):

while [ 1 ] ; do
    <cmd>
    sleep <x>
    echo ">>>>>>>>>>>>>" `date` ">>>>>>>>>>>>>>"
done

Chỉnh sửa: Tôi đã xóa một số "." trong tuyên bố tiếng vang cuối cùng ... tiếp quản từ những ngày Perl của tôi;)


3
while [ 1 ]chỉ hoạt động vì 1 được coi là một chuỗi, giống như while [ -n 1 ]. while [ 0 ]hoặc while [ jeff ]sẽ làm điều tương tự. while truecó ý nghĩa hơn nhiều
Mikel

3

Nếu ý định của bạn không phải là hiển thị một thông báo ra màn hình của bạn và nếu bạn có đủ khả năng để lặp lại công việc trong vài phút, thì crontab , có lẽ, sẽ là công cụ tốt nhất của bạn. Ví dụ: nếu bạn muốn thực thi lệnh của mình mỗi phút, bạn sẽ viết một cái gì đó như thế này trong crontabtệp của mình :

* * * * * my_precious_command

Vui lòng xem hướng dẫn để biết thêm ví dụ. Ngoài ra, bạn có thể dễ dàng đặt thời gian bằng cách sử dụng Trình tạo mã Crontab .


3

Nếu chúng ta có cả hai thì sao?

Đây là ý tưởng: chỉ với "khoảng", nó lặp đi lặp lại mãi mãi. Với "khoảng" và "lần", nó lặp lại số lần này, cách nhau bởi "khoảng".

Việc sử dụng :

$ loop [interval [times]] command

Vì vậy, thuật toán sẽ là:

  • Danh sách mục
  • nếu $ 1 chỉ chứa các chữ số, thì đó là khoảng (mặc định 2)
  • nếu $ 2 chỉ chứa các chữ số, thì đó là số lần (vô hạn mặc định)
  • vòng lặp while với các tham số này
    • "ngủ"
    • nếu một số lần đã được đưa ra, hãy giảm một var cho đến khi đạt được

Vì thế :

loop() {
    local i=2 t=1 cond

    [ -z ${1//[0-9]/} ] && i=$1 && shift
    [ -z ${1//[0-9]/} ] && t=$1 && shift && cond=1
    while [ $t -gt 0 ]; do 
        sleep $i
        [ $cond ] && : $[--t]
        $@
    done
}

Lưu ý: nó không hoạt động với phao, mặc dù sleepkhông chấp nhận chúng.
Baronsed

2

Tôi cố gắng tạo ra một biến thể trên câu trả lời của swalog. Với anh ấy, bạn phải đợi X giây cho lần lặp đầu tiên, tôi cũng đang chạy ở phía trước nên ..

./foo.sh;while sleep 1; do (./foo.sh) ; done

1

Nhanh chóng, bẩn thỉu và có thể nguy hiểm khi khởi động, nhưng nếu bạn thích phiêu lưu và biết những gì bạn đang làm, hãy đặt nó vào repeat.shchmod 755nó,

while true
do 
    eval $1 
    sleep $2 
done

Gọi nó với ./repeat.sh <command> <interval>

Ý nghĩa spidey của tôi nói rằng đây có lẽ là một cách xấu xa để làm điều này, là ý nghĩa spidey của tôi phải không?


8
Eval!? Để làm gì? sleep $1; shift; "$@"hoặc tương tự sẽ tốt hơn nhiều.
Mikel

Tại sao đây là -2. eval có thể là quá mức cần thiết ... ngoại trừ khi bạn cần nó. Eval cho phép bạn có được những thứ như chuyển hướng lên dòng lệnh.
Johan

1

Bạn có thể chạy tập lệnh từ init (thêm một dòng vào / etc / inittab). Kịch bản này phải chạy lệnh của bạn, ngủ trong thời gian bạn muốn đợi cho đến khi chạy lại tập lệnh và thoát nó. Ban đầu sẽ bắt đầu lại kịch bản của bạn sau khi thoát.


1

Cách dễ dàng để lặp lại một công việc từ crontab với khoảng thời gian dưới một phút (ví dụ 20 giây):

crontab: * * * * * script.sh

script.sh:

#!/bin/bash
>>type your commands here.

sleep 20
>>retype your commands here.

sleep 20
>>retype your commands here.

1

Bạn có thể có được sức mạnh từ trình thông dịch python từ shell.

python3 -c "import time, os
while True:
    os.system('your command')
    time.sleep(5)

thay thế năm bất cứ lúc nào (giây) bạn muốn.

Chú ý: trả về sử dụng SHIFT + ENTER để trả về đầu vào trong shell. Và đừng quên gõ 4 SPACE thụt lề trong mỗi dòng trong vòng lặp while.


Lưu ý rằng python's os.system()thực hiện một vỏ để giải thích các dòng lệnh, vì vậy cách gọi pythonđể chạy vỏ trong vòng một để chạy một lệnh định kỳ là một chút quá mức cần thiết. Bạn cũng có thể làm mọi thứ trong vỏ.
Stéphane Chazelas

Tôi đồng ý với bạn. Tuy nhiên, có một giấc ngủ 5 giây như bạn mô tả trong mỗi vòng lặp. Do đó, chi phí bổ sung từ việc bắt đầu một vỏ mới có thể được bỏ qua. Nếu chi phí này làm cho khoảng thời gian dài hơn mong đợi, bạn có thể giảm một số thời gian ngủ theo yêu cầu.
pah8J

0

Giải pháp này hoạt động trong MacOSX 10.7. Nó hoạt động tuyệt vời.

bash -c 'while [ 0 ]; do \
      echo "I will not automate this punishment in absurdum."; done'

Trong trường hợp của tôi

bash -c 'while [ 0 ]; do ls; done'

hoặc là

bash -c 'while [ 0 ]; do mv "Desktop/* Documents/Cleanup"; done'

để dọn dẹp máy tính để bàn của tôi liên tục.


1
Bạn có nghĩa là có một sleeplệnh ở đó?
Keith Thompson

4
while [ 0 ]là một cách kỳ lạ để viết một vòng lặp vô hạn; nó hoạt động vì testlệnh (còn được gọi là [) coi một chuỗi không trống là đúng. while : ; do ... ; donelà thành ngữ hơn, hoặc nếu bạn thích bạn có thể sử dụng while true ; do ... ; done.
Keith Thompson

0

Bạn có thể nguồn hàm đệ quy này:

#!/bin/bash
ininterval () {
    delay=$1
    shift
    $*
    sleep $delay
    ininterval $delay $*
}

hoặc thêm:

ininterval $*

và gọi kịch bản.


0

Để liên tục chạy một lệnh trong cửa sổ giao diện điều khiển, tôi thường chạy một cái gì đó như thế này:

while true; do (run command here); done

Điều này cũng hoạt động cho nhiều lệnh, ví dụ, để hiển thị đồng hồ cập nhật liên tục trong cửa sổ giao diện điều khiển:

while true; do clear; date; sleep 1; done


Tại sao các downvote? Bạn có thể thấy đây là một giải pháp hoạt động bằng cách sao chép và dán ví dụ vào cửa sổ terminal.
Thomas Bratt

Tôi nghĩ rằng bạn đã bỏ phiếu vì nó hơi nguy hiểm và tiêu tốn CPU.
ojblass

0
#! /bin/sh

# Run all programs in a directory in parallel
# Usage: run-parallel directory delay
# Copyright 2013 by Marc Perkel
# docs at http://wiki.junkemailfilter.com/index.php/How_to_run_a_Linux_script_every_few_seconds_under_cron"
# Free to use with attribution

if [ $# -eq 0 ]
then
   echo
   echo "run-parallel by Marc Perkel"
   echo
   echo "This program is used to run all programs in a directory in parallel" 
   echo "or to rerun them every X seconds for one minute."
   echo "Think of this program as cron with seconds resolution."
   echo
   echo "Usage: run-parallel [directory] [delay]"
   echo
   echo "Examples:"
   echo "   run-parallel /etc/cron.20sec 20"
   echo "   run-parallel 20"
   echo "   # Runs all executable files in /etc/cron.20sec every 20 seconds or 3 times a minute."
   echo 
   echo "If delay parameter is missing it runs everything once and exits."
   echo "If only delay is passed then the directory /etc/cron.[delay]sec is assumed."
   echo
   echo 'if "cronsec" is passed then it runs all of these delays 2 3 4 5 6 10 12 15 20 30'
   echo "resulting in 30 20 15 12 10 6 5 4 3 2 executions per minute." 
   echo
   exit
fi

# If "cronsec" is passed as a parameter then run all the delays in parallel

if [ $1 = cronsec ]
then
   $0 2 &
   $0 3 &
   $0 4 &
   $0 5 &
   $0 6 &
   $0 10 &
   $0 12 &
   $0 15 &
   $0 20 &
   $0 30 &
   exit
fi

# Set the directory to first prameter and delay to second parameter

dir=$1
delay=$2

# If only parameter is 2,3,4,5,6,10,12,15,20,30 then automatically calculate 
# the standard directory name /etc/cron.[delay]sec

if [[ "$1" =~ ^(2|3|4|5|6|10|12|15|20|30)$ ]]
then
   dir="/etc/cron.$1sec"
   delay=$1
fi

# Exit if directory doesn't exist or has no files

if [ ! "$(ls -A $dir/)" ]
then
   exit
fi

# Sleep if both $delay and $counter are set

if [ ! -z $delay ] && [ ! -z $counter ]
then
   sleep $delay
fi

# Set counter to 0 if not set

if [ -z $counter ]
then
   counter=0
fi

# Run all the programs in the directory in parallel
# Use of timeout ensures that the processes are killed if they run too long

for program in $dir/* ; do
   if [ -x $program ] 
   then
      if [ "0$delay" -gt 1 ] 
      then
         timeout $delay $program &> /dev/null &
      else
         $program &> /dev/null &
      fi
   fi
done

# If delay not set then we're done

if [ -z $delay ]
then
   exit
fi

# Add delay to counter

counter=$(( $counter + $delay ))

# If minute is not up - call self recursively

if [ $counter -lt 60 ]
then
   . $0 $dir $delay &
fi

# Otherwise we're done

0

Với zshvà một sleeptriển khai chấp nhận các đối số dấu phẩy động:

typeset -F SECONDS=0 n=0
repeat 100 {cmd; sleep $(((n+=3) - SECONDS))}

Hoặc cho forever:

for ((;;)) {cmd; sleep $(((n+=3) - SECONDS))}

Nếu bạn sleepkhông hỗ trợ nổi, bạn luôn có thể định nghĩa lại nó như là một wrapper xung quanh zsh's zselectBUILTIN:

zmodload zsh/zselect
sleep() zselect -t $((($1 * 100) | 0))

-1

... Tôi tự hỏi có bao nhiêu giải pháp phức tạp có thể được tạo ra khi giải quyết vấn đề này.

Nó có thể rất dễ dàng ...

open /etc/crontab 

đặt 1 dòng tiếp theo vào cuối tập tin như:

*/NumberOfSeconds * * * * user /path/to/file.sh

Nếu bạn muốn chạy một cái gì đó cứ sau 1 giây, chỉ cần đặt ở đó:

*/60 * * * * root /path/to/file.sh 

where that file.sh could be chmod 750 /path/to/file.sh

và bên trong file.sh nên là:

#!/bin/bash 
#What does it do
#What is it runned by

your code or commands

và đó là tất cả!

THƯỞNG THỨC!


6
Điều này LAF không đúng. * / 60 sẽ chạy mỗi 60 phút và * / 5, ví dụ, cứ sau 5 phút.
mika

1
giây không được phép trên crontab
GAD3R

kiểm tra công cụ của bạn trước khi trả lời với shit sai.
sjas

-1
until ! sleep 60; do echo $(date); command; command; command; done

làm việc cho tôi

  1. Tôi không cần chính xác cứ sau 60 giây
  2. "xem" không xem lệnh trên tất cả các hệ thống
  3. "xem" chỉ có thể nhận một lệnh (hoặc tệp tập lệnh)
  4. Đặt giấc ngủ trong điều kiện thay vì cơ thể làm cho vòng lặp bị gián đoạn tốt hơn (vì vậy, một yêu cầu trả lời cho một câu hỏi tương tự trên stackoverflow. Thật không may tôi có thể tìm thấy nó vào lúc này)
  5. Tôi muốn thực hiện nó một lần trước khi trì hoãn, vì vậy tôi sử dụng cho đến khi thay vì

Tôi chỉ nhận thấy rằng "cho đến khi" rõ ràng thực hiện giấc ngủ trước khi lặp lại đầu tiên quá. Có cách nào để đặt điều kiện ở cuối vòng lặp trong bash không?
Tít
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.