Powtarzaj komendę Uniksa co x sekundy na zawsze

480

Istnieje wbudowane polecenie Unix, repeatktórego pierwszym argumentem jest liczba powtórzeń polecenia, przy czym polecenie (z dowolnymi argumentami) jest określone przez pozostałe argumenty repeat.

Na przykład,

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

wyśle ​​echo podany ciąg 100 razy, a następnie zatrzyma się.

Chciałbym podobną komendę - nazwijmy ją forever- która działa podobnie, z tym wyjątkiem, że pierwszym argumentem jest liczba sekund na pauzę między powtórzeniami i powtarza się na zawsze. Na przykład,

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

Pomyślałem, że zapytam, czy coś takiego istnieje, zanim to piszę. Wiem, że to jest jak 2-liniowy skrypt Perla lub Pythona, ale może jest to bardziej standardowy sposób. Jeśli nie, możesz opublikować rozwiązanie w swoim ulubionym języku skryptowym, w stylu Rosetta Stone .

PS: Być może lepszym sposobem na zrobienie tego byłoby uogólnienie repeatpolegające zarówno na powtarzaniu liczby powtórzeń (z -1 oznacza nieskończoność), jak i na liczbie sekund snu między powtórzeniami. Powyższe przykłady stałyby się wówczas:

% repeat 100 0 echo "I will not automate this punishment."
% repeat -1 5 echo "This will get echoed every 5 seconds forever."
dreeves
źródło
1
Dlaczego nie: powtórzyć [-t czas] [-n liczba] polecenie [argumenty ...]?
Jonathan Leffler
17
Chłodny. Niestety zarówno na moim Ubuntu i moja AIX powtarzać się nie znaleziono polecenia . Czy możesz zrobić type repeati dać mi znać, skąd pochodzi?
3
Jestem również na Ubuntu i nie mam zainstalowanej powtórki. Pomocne będą wszelkie informacje na temat instalacji.
Rory,
7
repeatjest wbudowanym poleceniem w csh i tcsh.
Keith Thompson
2
@KeithThompson, csh, tcsh i zsh . Chociaż jest to bardziej słowo kluczowe niż wbudowany
Stéphane Chazelas,

Odpowiedzi:

615

Wypróbuj watchpolecenie.

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

Po to aby:

watch -n1  command

uruchomi polecenie co drugi (dobrze, technicznie, każdy sekund plus czas potrzebny na commanduruchomienie jako watch(przynajmniej procpsi busyboximplementacje) po prostu śpi jedną sekundę między dwoma ciągami command), na zawsze.

Czy chcesz przekazać polecenie execzamiast sh -c, użyj -xopcji:

watch -n1 -x command

W systemie macOS można uzyskać watchz portów Mac :

port install watch

Lub możesz go uzyskać od Homebrew :

brew install watch
Gregor Brandt
źródło
2
możesz użyć MacPorts, aby go zainstalować. sudo port install watch
10
Dostępne również w wersji Homebrew (zegarek instalujący napar).
Lyle
28
+1 Za nowe potężne narzędzie. Kiedyś while true; do X; sleep Y; done- Jest o wiele lepiej.
Adam Matan
4
Problem z tym narzędziem (przynajmniej na Ubuntu) polega na tym, że wymusza on tryb pełnoekranowy, więc jeśli wyrzucam jakieś okresowe informacje, tracę poprzednie.
scorpiodawg
4
Nazywa się cmdwatchna FreeBSD (z portów:) sysutils/cmdwatch.
Ouki,
243

Grzmotnąć

while+ sleep:

while true
do 
    echo "Hi"
    sleep 1
done

Oto to samo, co skrócona jedna linijka (z poniższych komentarzy):

while sleep 1; do echo "Hi"; done

Używa ;oddzielnych poleceń i używa sleep 1do whiletestu, ponieważ zawsze zwraca true. Możesz umieścić więcej poleceń w pętli - wystarczy je oddzielić;

Błąd składni
źródło
2
Uważam, że działa również jak napisano w ogólnej powłoce Bourne'a.
dmckee,
5
@dreeves, używając „;” jako separator, polecenia mogą być wykonywane w jednym wierszu. Spójrz na odpowiedź
Toony'ego
56
tryb uśpienia 1 zawsze zwraca wartość true, dzięki czemu można go wykorzystać jako warunek „while”. Nie jest to najbardziej czytelna rzecz na świecie, ale absolutnie uzasadniona: podczas snu 1; wykonaj echo „Cześć”; zrobione
David Costa
3
@David Costa: Masz rację, ale sleepnie zawsze powraca prawda. Użyłem takich pętli (choć z większym opóźnieniem), ponieważ istnieje sposób, aby zakończyć je z wdziękiem zabijając proces uśpienia.
Incnis Mrsi,
2
@IncnisMrsi Nie pomyślałem o tym, w rzeczywistości zwraca zero tylko po upływie czasu i niezerowe, gdy jest zakończone sygnałem. Dzięki za zwrócenie na to uwagi
David Costa,
71

Jest to tylko krótsza wersja innych while+sleepodpowiedzi, jeśli często wykonujesz tego rodzaju zadania jako codzienną rutynę, użycie tego pozwala uniknąć niepotrzebnych naciśnięć klawiszy, a jeśli wiersz poleceń zaczyna rozumieć dłużej, jest to nieco łatwiejsze. Ale ten zaczyna się od spania jako pierwszego.

Jest to na ogół przydatne, jeśli chcesz podążać za czymś, co ma wyjście w jednym wierszu, np. Obciążenie maszyny:

while sleep 1; do uptime; done
Bekir Dogan
źródło
Tak. Powinno to zostać udokumentowane obok UUOC.
Johan
10
A jeśli chcesz, aby działał jak „zegarek”, możesz to zrobićwhile clear; do date; command;sleep 5; done
Johan
Wow, dzięki za while sleepkombinację, oszczędza naciśnięcia klawiszy!
George Y.
45

Jednym z problemów, na który natrafiły wszystkie dotychczasowe odpowiedzi, jest to, że czas wykonania polecenia może dryfować. Na przykład, jeśli wykonujesz sleep 10polecenie między, a wykonanie polecenia zajmuje 2 sekundy, będzie ono uruchamiane co 12 sekund; jeśli uruchomienie zajmuje zmienną ilość czasu, to w dłuższej perspektywie czas jego uruchomienia może być nieprzewidywalny.

To może być właśnie to, czego chcesz; jeśli tak, skorzystaj z jednego z pozostałych rozwiązań lub skorzystaj z tego, ale uprość sleeppołączenie.

W przypadku jednominutowej rozdzielczości zadania cron będą uruchamiane o określonej godzinie, niezależnie od tego, jak długo zajmuje każde polecenie. (W rzeczywistości uruchomione zostanie nowe zadanie crona, nawet jeśli poprzednie nadal działa).

Oto prosty skrypt Perla, który śpi do następnego interwału, więc na przykład w odstępie 10 sekund polecenie może zostać uruchomione o 12:34:00, 12:34:10, 12:34:20 itd., Nawet jeśli samo polecenie zajmuje kilka sekund. Jeśli polecenie działa dłużej niż intervalsekundy, następny przedział zostanie pominięty (w przeciwieństwie do crona). Przedział jest obliczany względem epoki, więc przedział 86400 sekund (1 dzień) będzie działał o północy czasu 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?)
}
Keith Thompson
źródło
Zobacz także inne pytanie
Stéphane Chazelas,
1
prostsze w bash, aby zminimalizować znoszenie (chociaż może dryfować po bardzo długich oknach użytkowania z powodu polecenia uśpienia i samo bash gromadzi swoje małe czasy wykonania): while :; do sleep 1m & command; wait; done
matematyka
1
@math: Clever. Nie działa, jeśli w bieżącej powłoce działają inne procesy działające w tle ( waitzaczekają na wszystkie). Można to naprawić, zmieniając waitna wait $!, gdzie $!jest PID sleepprocesu. Opublikuj to jako odpowiedź, a ja go poprę. Ale generuje trochę obcego wyniku w interaktywnej powłoce.
Keith Thompson,
29

Myślę, że wszystkie dotychczasowe odpowiedzi są albo zbyt skomplikowane, albo zamiast tego odpowiedz na inne pytanie:

  • „Jak kilkakrotnie uruchamiać program, tak aby między końcem programu a następnym uruchomieniem było X sekund opóźnienia” .

Prawdziwe pytanie brzmiało:

  • „Jak uruchamiać program co X sekund”

Są to dwie bardzo różne rzeczy, gdy polecenie wymaga czasu na zakończenie.

Weźmy na przykład skrypt foo.sh(udawaj, że jest to program, który zajmuje kilka sekund).

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

Chcesz uruchomić to co sekundę, a większość sugerowałaby watch -n1 ./foo.sh, lub while sleep 1; do ./foo.sh; done. Daje to jednak wynik:

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

Co nie jest dokładnie uruchamiane co sekundę. Nawet z -pflagą, jak man watchsugeruje strona, może to rozwiązać, wynik jest taki sam.

Łatwym sposobem na wykonanie pożądanego zadania, na które niektórzy się dotykają, jest uruchomienie polecenia w tle. Innymi słowy:

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

I to wszystko.

Możesz uruchomić go co 500 ms z sleep 0.5, lub co masz.

swalog
źródło
1
Ci, którzy głosowali na różne odpowiedzi, nie rozumieją elegancji twojej odpowiedzi ...
Serge Stroobandt
Jest to dobre, chyba że twój program ma skutki uboczne. Jeśli program trwa dłużej niż interwał, działania niepożądane mogą zakłócać (np. Nadpisywanie pliku). Jeśli program czeka na coś innego, a coś innego trwa wiecznie, wtedy zaczynasz coraz więcej zadań w tle na czas nieokreślony. Używaj ostrożnie.
John Moeller
4
może odwrócić kolejność tła, aby mieć pewność, że jednocześnie uruchamiany jest nie więcej niż jeden plik ./foo.sh, ale nie szybciej niż 1 na sekundę: while :; do sleep 1 & ./foo.sh; wait; done
matematyka
Tak, będzie dryfował, kilka milisekund w każdej pętli, ale będzie. Dokonać date +"%H:%M:%S.%N"w foo.sh zobaczyć jak frakcja będzie powoli wzrastać na każdej pętli.
Izaak
Tak, to prawda, że ​​większość odpowiedzi nie dotyczy dosłownie pytania. Ale to prawie bezpieczny zakład, że odpowiedzą na to, czego chciał PO. Byłoby bezpieczniejszym zakładem, gdyby OP faktycznie zaakceptował odpowiedź ... Ale to dobrze, pod warunkiem, że zdajesz sobie sprawę z możliwych skutków ubocznych i masz pewność, że średni czas wykonywania polecenia nie przekroczy czasu cyklu
Auspex
17

W bash:

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

( echomożna zastąpić dowolnym poleceniem ...

Lub w perl:

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

Gdzie print "I will not automate this punishment in absurdum.\n"można zastąpić „dowolnym” poleceniem otoczonym backticks (`).

I na chwilę dodaj sleepinstrukcję w pętli for:

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

i

perl -e 'for (;1;) {print "I will not automate this punishment in absurdum.\n"; sleep 1}'
Kevin
źródło
16
while [ 0 ]to zły sposób na napisanie tego. Oznacza 0to, że jest łańcuchem o długości> 0. while [ 1 ]Lub while [ jeff ]zrobiłby to samo. Lepiej pisać while true.
Mikel
5
@Mikel: Lubwhile :
Keith Thompson
10

Ostatnie bash> = 4.2 pod najnowszym jądrem Linuksa, odpowiedź oparta.

Aby ograniczyć czas wykonania, nie ma widelców ! Używane są tylko wbudowane.

W tym celu używam readwbudowanej funkcji zamiast sleep. Niestety nie będzie to działać w przypadku sesji notty .

Funkcja szybkiego repeat” zgodnie z żądaniem:

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
}

Mały test z podanymi ciągami:

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

W zależności od szczegółowości i czasu trwania przesłanego polecenia ...

W najnowszych jądrach Linuksa istnieje plik /proc/timer_listzawierający informacje o czasie w nanosekundach.

Jeśli chcesz uruchomić polecenie dokładnie raz na sekundę, Twoje polecenie musi zakończyć się w niecałą sekundę! I stamtąd musisz sleeptylko resztę bieżącej sekundy.

Jeśli opóźnienie jest ważniejsze, a twoje polecenie nie wymaga znacznego czasu, możesz:

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

Ale jeśli Twoim celem jest uzyskanie drobniejszej ziarnistości, musisz:

Użyj informacji w nanosekundach, aby poczekać do początku sekundy ...

W tym celu napisałem małą funkcję bash:

# 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
}

Po ich uzyskaniu możesz:

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

uruchomić ${command[@]}bezpośrednio w wierszu poleceń, niż porównać z

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

to musi dać dokładnie taki sam wynik.

Wynajmuje funkcję repeat” zgodnie z żądaniem:

Możesz to źródło:

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
}

Następnie spróbuj:

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
F. Hauri
źródło
read -tjest wbudowany , a sleepnie jest. Może to mieć efekt graniczny (tj. Uderzenie [klawisz: powrót] cicho przerywa sen ), ale można to wyleczyć, testując$foo
F. Hauri
Bardzo imponująca odpowiedź
gena2x
4

Chcesz wypróbować to (Bash)?

forever ()   {
    TIMES=shift;
    SLEEP=shift;
    if [ "$TIMES" = "-1" ]; then  
        while true;
        do 
            $@
            sleep $SLEEP
        done
    else
        repeat "$TIMES" $@ 
    fi; }
człowiek w pracy
źródło
Nie jestem za bardzo na muszli, więc ulepszenia są mile widziane. I wydaje mi się, że w mojej dystrybucji nie ma polecenia „powtarzania”.
2
repeatjest specyficzny dla csh i tcsh.
Keith Thompson
2
@KeithThompson: and 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); 
}
dreeves
źródło
4

Bash i niektóre z jego pokrewnych powłok mają wygodny (( ... ))zapis, w którym można oceniać wyrażenia arytmetyczne.

Tak więc, jako odpowiedź na twoje trzecie wyzwanie, w którym zarówno liczba powtórzeń, jak i opóźnienie między nimi powinny być konfigurowalne, oto jeden ze sposobów, aby to zrobić:

repeat=10
delay=1

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

Ta odpowiedź cierpi również na przesunięcie czasowe ujęte w odpowiedzi Keitha .

Thor
źródło
To był najlepszy.
Ahmad Awais
3

Jak wspomniano przez gbrandt, jeśli watchpolecenie jest dostępne, zdecydowanie użyj go. Jednak niektóre systemy uniksowe nie mają zainstalowanej domyślnie (przynajmniej nie działają tam, gdzie pracuję).

Oto inne rozwiązanie z nieco inną składnią i wyjściem (działa w BASH i SH):

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

Edycja: usunąłem część „.” w ostatnim zestawieniu echa ... pozostałość po moich dniach Perla;)

Kevin
źródło
3
while [ 1 ]działa tylko, ponieważ 1 jest traktowany jak ciąg znaków, tak jak while [ -n 1 ]. while [ 0 ]lub while [ jeff ]zrobiłby to samo. while truema znacznie większy sens.
Mikel
3

Jeśli nie zamierzasz wyświetlać komunikatu na ekranie i możesz sobie pozwolić na powtórzenie zadania w minutach , być może najlepszym narzędziem będzie crontab . Na przykład, jeśli chcesz wykonywać polecenie co minutę, w crontabpliku zapisujesz coś takiego :

* * * * * my_precious_command

Zapoznaj się z samouczkiem dla dalszego przykładu. Ponadto możesz łatwo ustawić czasy za pomocą Crontab Code Generator .

Barun
źródło
3

Co jeśli mielibyśmy oba?

Oto pomysł: tylko „interwał” powtarza się na zawsze. Z „interwałem” i „czasem” powtarza tę liczbę razy, oddzielonych „interwałem”.

Użycie :

$ loop [interval [times]] command

Algorytm będzie więc:

  • Element listy
  • jeśli $ 1 zawiera tylko cyfry, jest to interwał (domyślnie 2)
  • jeśli 2 $ zawiera tylko cyfry, jest to liczba razy (domyślnie nieskończona)
  • podczas pętli z tymi parametrami
    • „przerwa” snu
    • jeśli podano kilka razy, zmniejsz var aż do jego osiągnięcia

W związku z tym :

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
}
Baronizowany
źródło
Uwaga: nie działa z liczbami zmiennoprzecinkowymi, mimo sleepże je akceptuje.
Baronizowany
2

Skończyło się na tym, że stworzyłem wariant odpowiedzi Swalog. Z nim musiałeś poczekać X sekund na pierwszą iterację, ja również prowadzę moją na pierwszym planie, więc ...

./foo.sh;while sleep 1; do (./foo.sh) ; done
Duane Lortie
źródło
1

Szybkie, brudne i prawdopodobnie niebezpieczne do uruchomienia, ale jeśli masz ochotę na przygodę i wiesz, co robisz, włóż to repeat.shi chmod 755to,

while true
do 
    eval $1 
    sleep $2 
done

Wywołaj to za pomocą ./repeat.sh <command> <interval>

Mój paskudny zmysł mówi, że jest to prawdopodobnie zły sposób, czy mój pajęczy zmysł, prawda?

mallyone
źródło
8
Eval !? Po co? sleep $1; shift; "$@"lub podobny byłby znacznie lepszy.
Mikel,
Nie wiem, dlaczego to jest -2. eval może być przesadą ... chyba że jest to potrzebne. Eval pozwala uzyskać takie rzeczy jak przekierowanie do wiersza poleceń.
Johan
1

Możesz uruchomić skrypt z init (dodając wiersz do / etc / inittab). Ten skrypt musi uruchomić polecenie, spać przez czas, który chcesz poczekać, aż uruchom ponownie skrypt i wyjść z niego. Init uruchomi skrypt ponownie po wyjściu.

Roberto Paz
źródło
1

Łatwy sposób na powtórzenie zadania z crontab w odstępie krótszym niż jedna minuta (przykład 20 sekund):

crontab: * * * * * script.sh

script.sh:

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

sleep 20
>>retype your commands here.

sleep 20
>>retype your commands here.
Charles Nakhel
źródło
1

Moc można uzyskać z interpretera Pythona z powłoki.

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

wymień pięć w dowolnym czasie (sek.), jaki chcesz.

Uwaga: return użyj SHIFT + ENTER, aby zwrócić dane wejściowe w powłoce. I nie zapomnij wpisać wcięcia 4 SPACJA w każdej linii w pętli while.

pah8J
źródło
Zauważ, że pythons os.system()wykonuje powłokę w celu interpretacji wiersza poleceń, więc wywoływanie pythonpowłoki w pętli w celu okresowego uruchamiania polecenia jest nieco przesadzone. Równie dobrze możesz zrobić wszystko w powłoce.
Stéphane Chazelas
Zgadzam się z Tobą. Jednak w każdej pętli występuje 5-sekundowy sen. W ten sposób dodatkowy koszt związany z uruchomieniem nowej powłoki można zignorować. Jeśli koszt ten wydłuży okres oczekiwania, możesz zmniejszyć wymagany czas snu.
pah8J
0

To rozwiązanie działa w systemie MacOSX 10.7. Działa cudownie.

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

W moim przypadku

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

lub

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

aby stale sprzątać mój pulpit.

Dan Ruiz
źródło
1
Miałeś na myśli sleepkomendę?
Keith Thompson
4
while [ 0 ]jest dziwnym sposobem na napisanie nieskończonej pętli; działa, ponieważ testpolecenie (znane również jako [) traktuje niepusty ciąg znaków jako prawdziwy. while : ; do ... ; donejest bardziej idiomatyczny lub jeśli wolisz, możesz go użyć while true ; do ... ; done.
Keith Thompson
0

Możesz użyć tej funkcji rekurencyjnej:

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

lub dodaj:

ininterval $*

i wywołaj skrypt.

nieznany użytkownik
źródło
0

Aby wielokrotnie uruchamiać polecenie w oknie konsoli, zwykle uruchamiam coś takiego:

while true; do (run command here); done

Działa to również w przypadku wielu poleceń, na przykład w celu wyświetlania stale aktualizowanego zegara w oknie konsoli:

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

Thomas Bratt
źródło
Dlaczego głosowanie negatywne? Możesz zobaczyć, że jest to działające rozwiązanie, kopiując i wklejając przykład do okna terminala.
Thomas Bratt
Myślę, że przegłosowano, ponieważ jest to trochę niebezpieczne i zużywa procesor.
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
użytkownik56318
źródło
0

Za pomocą zshi sleepimplementacji, która akceptuje argumenty zmiennoprzecinkowe:

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

Lub dla forever:

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

Jeśli urządzenie sleepnie obsługuje pływaków, zawsze można zdefiniować ją jako owinięcia wokół zsh„s zselectwbudowanego:

zmodload zsh/zselect
sleep() zselect -t $((($1 * 100) | 0))
Stéphane Chazelas
źródło
-1

... Zastanawiam się, ile skomplikowanych rozwiązań można stworzyć, rozwiązując ten problem.

To może być takie proste ...

open /etc/crontab 

umieść tam 1 następną linię na końcu pliku, np .:

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

Jeśli chcesz uruchomić coś co 1 sekundę, po prostu umieść tam:

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

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

a wewnątrz tego pliku.sh powinno być:

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

your code or commands

i to wszystko!

CIESZYĆ SIĘ!

Mirra
źródło
6
To nie jest poprawne. * / 60 będzie uruchamiany co 60 minut, a * / 5, na przykład co 5 minut.
mika
1
sekundy nie są dozwolone w crontab
GAD3R
przetestuj swoje rzeczy, zanim odpowiesz złym gównem.
sjas
-1
until ! sleep 60; do echo $(date); command; command; command; done

pracuje dla mnie.

  1. Nie potrzebuję tego dokładnie co 60 sekund
  2. „watch” nie obserwuje poleceń we wszystkich systemach
  3. „zegarek” może przyjąć tylko jedno polecenie (lub plik skryptu)
  4. ustawienie snu w stan zamiast ciała powoduje, że pętla jest lepiej przerywalna (więc twierdzenie w odpowiedzi na podobne pytanie dotyczące przepełnienia stosu. niestety w tej chwili mogę je znaleźć)
  5. Chcę go wykonać raz przed opóźnieniem, więc używam do czasu zamiast
Tytus
źródło
Właśnie zauważyłem, że „dopóki” najwyraźniej nie wykonuje snu również przed pierwszą iteracją. czy jest jakiś sposób na umieszczenie warunku na końcu pętli w bash?
Titus