Cần một vòng lặp để ngủ trong một phần của giây


13

Trên máy tính của tôi, tôi cần thực hiện một chu trình lặp lại 1 lệnh đơn giản phải có độ trễ được biểu thị bằng phân số thứ hai.

Hãy nói rằng tôi cần:

  • để lưu một tệp với sự tăng cường (tệp-0, tệp-1, tệp-2, ...) được tạo bởi một cái gì đó tầm thường cho ví dụ này như time > file-$x
  • Tôi cần phải làm điều này cứ sau 1/70 giây (làm ví dụ) vì tôi muốn thể hiện thời gian của mình bằng các phân số của giây.

Làm thế nào tôi có thể thực sự chính xác và có mọi thứ được thể hiện bằng một tập lệnh bash?

Phân số có thể tạo ra một số lượng không xác định, tôi cần phải chính xác và vì vậy tôi cần ít nhất 4-5 số thập phân.

Câu trả lời:


11

Để chuyển đổi từ phân số thành số thập phân trong bash, hãy làm một cái gì đó như

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

Sau đó, để thực hiện một vòng lặp trên giá trị đó,

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

Việc triển khai giấc ngủ của tôi trên Debian (GNU) dường như chấp nhận các giá trị giấc ngủ thập phân.

Không may..

Với độ chính xác đó (4-5 vị trí thập phân), bạn sẽ muốn một cái gì đó như tập lệnh perl hoặc chương trình được biên dịch; chi phí chung của việc gọi bất kỳ chương trình nào trong vòng lặp sẽ thêm rất nhiều jitter. Gọi giấc ngủ sẽ mất vài mili giây.

Hãy xem xét những điều sau đây, 1 / 10.000 giây của giấc ngủ thứ hai, được thực hiện 1000 lần:

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

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

Kết quả dự kiến ​​sẽ là 1/10 giây. Giấc ngủ không có nơi nào gần với dung sai bạn muốn.

/programming/896904/how-do-i-s ngủ-for-a-millisecond-in-perl

sử dụng Thời gian của perl :: HiRes, 1000 * 1000 micro giây:

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

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

cho chúng ta gần hơn một giây.


nếu không thể đạt được 4-5 số thập phân, tôi sẽ lấy kết quả tốt nhất có thể thay thế, nhưng quan điểm của tôi là tôi cần diễn đạt điều này bằng phân số, không phải số thập phân hoặc giây.
dùng1717079

Việc chuyển một phân số thành số thập phân trong bất kỳ ngôn ngữ kịch bản nào, bao gồm cả bash là hoàn toàn tầm thường. ví dụ: echo "scale = 4; 5/10" | bc
Rob Bos

Bây giờ tôi đã có cách chuyển đổi biểu thức, vấn đề bây giờ là một bash đơn giản, nó rất chậm và không thể theo kịp tần số này ...
user1717079

Vâng, nếu bạn muốn bất cứ điều gì chính xác hơn nhiều so với khoảng 1/10 giây, có lẽ bạn sẽ muốn perl hoặc python để tránh chương trình gọi qua đầu trong vòng lặp.
Rob Bos

7

Có lẽ bạn chỉ cần chạy

sleep 0.7

?

man 1 sleep

trên bản phân archlinuxphối của tôi :

MÔ TẢ Tạm dừng trong SỐ giây. SUFFIX có thể là 's' trong vài giây (mặc định), 'm' trong vài phút, 'h' trong nhiều giờ hoặc 'd' trong nhiều ngày. Không giống như hầu hết các triển khai yêu cầu SỐ là một số nguyên, ở đây SỐ có thể là số dấu phẩy động tùy ý. Đưa ra hai hoặc nhiều đối số, tạm dừng trong khoảng thời gian được chỉ định bằng tổng giá trị của chúng.


sleepcho phép phân số? bạn có thể đưa ra một ví dụ hoàn chỉnh với một vòng lặp giả không?
dùng1717079

0,7 không phải là một phần thể hiện vấn đề của tôi ... vấn đề của tôi là về độ chi tiết; suy nghĩ về 1/3 và cố gắng chính xác với giấc ngủ.
dùng1717079

6

Sinh ra một quy trình và tải một tệp thực thi mới trong đó có thể sẽ mất vài giây trong một giây, do đó, độ chính xác đó không thực sự có ý nghĩa. Cũng lưu ý rằng thời gian CPU trên nhiều hệ thống được phân bổ cho các quy trình bằng các lát tối đa 10ms.

Phải nói rằng, một số sleeptriển khai có số phân số giây và cả zsh và ksh93 có thể làm cho $SECONDSbiến số đặc biệt của chúng với phân số typeset -F SECONDS.

Ví dụ (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

Rất tiếc, nó trôi dạt. Bạn có thể điều chỉnh thời gian ngủ dựa trên $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 triệu giây thêm đó có lẽ sẽ được tính để chạy lệnh cuối cùng sleepdatelệnh.

Cũng lưu ý rằng zsh có zselectnội dung dựng sẵn với thời gian chờ được biểu thị bằng một phần trăm giây. Và ksh93 đã tích sleephợp sẵn (và chấp nhận các dấu chấm động) và nó printfcó thể in ngày / lần.

$ 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

Nếu bạn muốn bất cứ điều gì chính xác hơn, có lẽ bạn sẽ muốn một hệ điều hành thời gian thực hoặc một hệ điều hành có khả năng thời gian thực chắc chắn không sử dụng hệ vỏ.


sleep 1/70không được phép trên máy của tôi ...
user1717079

thậm chí không gần với những gì tôi muốn đạt được, làm thế nào tôi có thể thực hiện mọi thứ nhanh hơn?
dùng1717079

Xin lỗi, theo phân số, tôi không có nghĩa là n / m trong đó n và m là số nguyên, nhưng là số thập phân với một phần phân số. Tôi cho rằng bạn cũng có thể gọi chúng là số dấu phẩy động thập phân mặc dù tôi không chắc về thuật ngữ tiếng Anh thích hợp
Stéphane Chazelas

tôi nghĩ rằng tôi sẽ tránh cái vỏ ...
user1717079

Người trả lời đúng. Vỏ và quy trình thường không phù hợp với độ phân giải mili giây. Để có hiệu suất đáng tin cậy, bạn sẽ cần phải biên dịch một chương trình gọi cho chúng tôi ngủ (3) hoặc tương tự - và bạn cũng có thể cần phải biên dịch lại kernel của mình với một cấu hình khác; hoặc ít nhất cung cấp các tham số khác nhau cho bộ lập lịch thời gian chạy của kernel. Điều này là không cần thiết để làm. Một bản cài đặt Debian tiêu chuẩn với kernel tiêu chuẩn có khả năng thời gian thực hạn chế. Tuy nhiên, bạn có thể muốn kiểm tra lệnh (1) đẹp; nó sẽ giúp.
thb

3

Nếu shell của sleepbạn không chấp nhận phân số, hãy sử dụng perl.

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

sleep_fraction 0.01428

Nếu bạn cần tìm ra phân số, sử dụng echo "scale=5; 1/70" | bc


0

Trong Alpine Linux (Busybox), bạn có thể lặp trong vài giây với usleep 10(tương đương với 0.00001một giây)

GNU sleep hỗ trợ phân số của một giây: sleep 0.00001

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.