Perlu putaran untuk tidur selama sepersekian detik


13

Pada mesin saya, saya perlu menjalankan siklus yang mengulang 1 perintah sederhana yang harus memiliki penundaan dinyatakan dalam sepersekian detik.

Katakanlah saya perlu:

  • untuk menyimpan file dengan enumaration yang meningkat (file-0, file-1, file-2, ...) dihasilkan oleh sesuatu yang sepele untuk contoh ini seperti time > file-$x
  • Saya perlu melakukan ini setiap 1/70 detik (sebagai contoh) karena saya ingin mengekspresikan waktu saya dengan pecahan detik.

Bagaimana saya bisa benar-benar tepat dan memiliki semua yang diungkapkan dengan skrip bash?

Fraksi dapat menghasilkan jumlah yang tidak dapat ditentukan, saya harus tepat dan jadi saya membutuhkan setidaknya 4-5 desimal.

Jawaban:


11

Untuk mengonversi dari pecahan menjadi desimal di bash, lakukan sesuatu seperti

myvar=$(echo "scale=4; 5/10" | bc)

Kemudian, untuk melakukan loop pada nilai itu,

for i in $(seq 1 1000); do sleep $myvar; done

Implementasi sleep saya di Debian (GNU) tampaknya menerima nilai sleep desimal.

Sayangnya..

Dengan ketepatan semacam itu (4-5 tempat desimal), Anda akan menginginkan sesuatu seperti skrip perl atau program yang dikompilasi; overhead memanggil program apa pun dalam loop akan menambah banyak jitter. Memanggil tidur itu sendiri akan memakan waktu beberapa milidetik.

Pertimbangkan hal berikut, 1/10 dari tidur kedua, dilakukan 1000 kali:

time for i in $(seq 1 1000); do sleep 0.0001; done

real    0m2.099s
user    0m3.080s
sys     0m1.464s

Hasil yang diharapkan adalah 1/10 detik. Tidur tidak memiliki toleransi yang Anda inginkan.

/programming/896904/how-do-i-sleep-for-a-millisecond-in-perl

menggunakan perl's Time :: HiRes, 1000 * 1000 microseconds:

my $i=0;
for($i=0;$i<=1000;$i++) {
        usleep(1000);
}

real    0m1.133s
user    0m0.024s
sys     0m0.012s

memberi kita lebih dekat ke detik.


jika tidak mungkin untuk mencapai 4-5 desimal saya akan mengambil hasil terbaik sebagai alternatif, tetapi poin saya adalah bahwa saya perlu mengungkapkan ini dalam fraksi, bukan desimal atau detik.
user1717079

Sangat sepele untuk mengonversi pecahan menjadi desimal dalam bahasa skrip apa pun, termasuk bash. misalnya gema "skala = 4; 5/10" | bc
Rob Bos

sekarang saya telah mendapatkan cara mengubah ekspresi, masalahnya sekarang adalah bahwa bash sederhana itu sangat lambat dan tidak bisa mengikuti frekuensi ini ...
user1717079

Ya, jika Anda menginginkan sesuatu yang jauh lebih tepat daripada sekitar 1/10 detik, Anda mungkin ingin perl atau python untuk menghindari program yang memanggil overhead dalam loop.
Rob Bos

7

Mungkin Anda bisa langsung lari

sleep 0.7

?

man 1 sleep

di archlinuxdistro saya :

DESKRIPSI Jeda selama NUMBER detik. SUFFIX mungkin 's' untuk detik (default), 'm' untuk menit, 'h' selama berjam-jam atau 'd' selama berhari-hari. Tidak seperti kebanyakan implementasi yang membutuhkan NUMBER menjadi bilangan bulat, di sini NUMBER dapat berupa angka floating point yang berubah-ubah. Diberikan dua atau lebih argumen, jeda untuk jumlah waktu yang ditentukan oleh jumlah nilainya.


sleepmemungkinkan pecahan? dapatkah Anda memberikan contoh lengkap dengan loop palsu?
user1717079

0,7 bukan sebagian kecil yang mengungkapkan masalah saya ... masalah saya adalah tentang rincian; pikirkan 1/3 dan cobalah untuk lebih tepat dengan tidur.
user1717079

6

Memunculkan proses dan memuat executable baru di dalamnya kemungkinan akan memakan waktu beberapa milidetik, sehingga ketepatan seperti itu tidak masuk akal. Perhatikan juga bahwa waktu CPU pada banyak sistem dialokasikan untuk proses dengan irisan hingga 10 ms.

Karena itu, beberapa sleepimplementasi mengambil bilangan pecahan detik, dan baik zsh dan ksh93 dapat membuat $SECONDSpecahan variabel khusus mereka typeset -F SECONDS.

Contoh (zsh):

$ typeset -F SECONDS=0; for ((i=1; i<=70; i++)); do sleep $((1./70)); date +%s.%N; done | { head -n3;echo ..;tail -n3; }; echo $SECONDS
1350076317.374870501
1350076317.391034397
1350076317.407278461
..
1350076318.464585550
1350076318.480887660
1350076318.497133050
1.1393780000

Ups, itu melayang. Anda dapat menyesuaikan waktu tidur berdasarkan $SECONDS:

$ typeset -F SECONDS=0; for ((i=1; i<=70; i++)); do sleep $((i/70. - SECONDS)); date +%s.%N; done | { head -n3;echo ...;tail -n3; }; echo $SECONDS
1350076420.262775654
1350076420.277012997
1350076420.291302750
../..
1350076421.219682227
1350076421.234134663
1350076421.248255685
1.0020580000

2 milidetik tambahan itu mungkin harus dipertanggungjawabkan untuk menjalankan yang terakhir sleepdan dateperintah.

Perhatikan juga bahwa zsh memiliki zselectbuiltin dengan batas waktu dinyatakan dalam seperseratus detik. Dan ksh93 memiliki sleepbuilt in (dan menerima floating point) dan printfdapat mencetak tanggal / waktu.

$ typeset -F SECONDS=0; for ((i=1; i<=70; i++)); do ((i<4 || i>67)) && printf '%(%S.%N)T\n' now; sleep $((i/70.-SECONDS)); done; echo $SECONDS
20.823349000
20.837510000
20.851663000
21.780099000
21.794254000
21.808405000
0.9992358685

Jika Anda menginginkan sesuatu yang lebih tepat, Anda mungkin menginginkan sistem operasi waktu nyata atau sistem operasi dengan kemampuan waktu nyata dan tentu saja tidak menggunakan shell.


sleep 1/70tidak diizinkan di mesin saya ...
user1717079

bahkan tidak dekat dengan apa yang ingin saya capai, bagaimana saya bisa menjalankan sesuatu lebih cepat?
user1717079

Maaf, dengan pecahan, maksud saya tidak dinyatakan sebagai n / m di mana n dan m adalah bilangan bulat, tetapi sebagai desimal dengan bagian pecahan. Saya kira Anda juga bisa memanggil mereka angka floating point desimal meskipun saya tidak yakin tentang terminologi bahasa Inggris yang tepat
Stéphane Chazelas

saya pikir saya akan menghindari shell ...
user1717079

Penjawabnya benar. Kerang dan proses biasanya tidak cocok untuk resolusi milidetik. Untuk kinerja yang andal, Anda harus mengkompilasi program yang memanggil usleep (3) atau sejenisnya - dan Anda mungkin perlu mengkompilasi ulang kernel Anda dengan konfigurasi yang berbeda, juga; atau paling tidak memasukkan parameter yang berbeda ke penjadwal runtime kernel Anda. Ini tidak perlu dilakukan. Instalasi Debian standar dengan kernel standar memiliki kemampuan realtime terbatas. Namun, Anda mungkin ingin memeriksa perintah (1) yang bagus; mungkin membantu.
thb

3

Jika shell sleepAnda tidak menerima pecahan, gunakan perl.

sleep_fraction() {
  /usr/bin/perl -e "select(undef, undef, undef, $1)"
}

sleep_fraction 0.01428

Jika Anda perlu mengetahui fraksi, gunakan echo "scale=5; 1/70" | bc


0

Di bawah Alpine Linux (Busybox) Anda dapat mengulang dengan mikrodetik usleep 10(setara dengan 0.00001satu detik)

GNU sleep mendukung sebagian kecil dari yang kedua: sleep 0.00001

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.