Làm thế nào để xác định lượng thời gian còn lại trong một giấc ngủ của người Haiti?


11

Tôi có:

sleep 210m && for i in $(seq 1 5); do echo -e '\a'; sleep 0.5; done 

chạy như một bộ đếm thời gian đơn giản, không rườm rà để nhắc nhở tôi khi nào nên làm gì đó. Đó sleep 210mlà PID 25347.

Tôi đang cố gắng tìm hiểu bao nhiêu thời gian còn lại trong giấc ngủ. Điều tốt nhất tôi nghĩ ra, với tôi đưa vào thời lượng ngủ ban đầu (210 phút) là:

$ echo "210 60 * $(ps -o etimes= 25347) - 60 ~ r n [:] P p" | dc
78:11

(Giải thích: Bit đầu tiên tính số giây trong giấc ngủ ban đầu; $(ps…)bit lấy thời gian kể từ khi giấc ngủ bắt đầu tính bằng giây, sau đó phần còn lại trừ chúng và hiển thị theo phút và giây.)

Nó xảy ra với tôi điều này sẽ hữu ích nói chung; Có cách nào tốt hơn để làm điều này? Hoặc ít nhất là một cách thông minh để phân tích thời gian ngủ từ ps -o args?


1
Lưu ý rằng các quy trình có thể (và thường làm) chạy nhiều hơn một lệnh trong vòng đời của chúng. Chẳng hạn, sh -c 'sleep 1; sleep 2'với nhiều shtriển khai, đó là cùng một quy trình thực thi shvà sau đó thực thi sleep 2(1 giây sau).
Stéphane Chazelas

@ StéphaneChazelas Tôi không nghĩ điều đó có thể xảy ra ở đây, có lẽ trình bao chỉ có thể thực hiện tối ưu hóa đó cho lệnh cuối cùng mà nó thực thi (vì nó không có cách nào để lấy lại quyền kiểm soát sau đó exec). Nhưng đó là một điểm tốt cho bất kỳ giải pháp chung.
derobert

Cũng lưu ý rằng một số shell ( mkshksh93ít nhất) đã tích sleephợp sẵn (vì vậy sẽ không xuất hiện ps)., Bạn cần biết trước những sleeptriển khai mà bạn đang xử lý.
Stéphane Chazelas

Câu trả lời:


5

Hỗ trợ các sleepđối số GNU hoặc Solaris 11 (một hoặc nhiều <double>[smhd]thời lượng, do đó cũng sẽ hoạt động với các triển khai truyền thống chỉ hỗ trợ một số nguyên thập phân (như trên FreeBSD), nhưng không hỗ trợ các đối số phức tạp hơn như thời lượng ISO-8601 ). Sử dụng etimethay vì etimesnhư là di động hơn (Unix tiêu chuẩn).

remaining_sleep_time() { # arg: pid
  ps -o etime= -o args= -p "$1" | perl -MPOSIX -lane '
    %map = qw(d 86400 h 3600 m 60 s 1);
    $F[0] =~ /(\d+-)?(\d+:)?(\d+):(\d+)/;
    $t = -($4+60*($3+60*($2+24*$1)));
    for (@F[2..$#F]) {
      s/\?//g;
      ($n, $p) = strtod($_);
      $n *= $map{substr($_, -$p)} if $p;
      $t += $n
    }
    print $t'
}

(những s/\?//glà để thoát khỏi những ?ký tự procps' pssử dụng để thay thế cho ký tự điều khiển. Nếu không có nó, nó sẽ thất bại trong việc phân tích sleep $'\r1d'hay sleep $'\t1d'... Thật không may, ở một số vùng, trong đó có Cmiền địa phương, nó sử dụng .thay vì ?. Không có nhiều chúng ta có thể làm trong trường hợp đó vì không có cách nào để nói với a \t5dtừ .5d(nửa ngày)).

Vượt qua pid làm đối số.

Điều đó cũng giả sử argv[0]thông qua sleepkhông chứa khoảng trống và số lượng đối số đủ nhỏ để nó không bị cắt bớt ps.

Ví dụ:

$ sleep infinity & remaining_sleep_time "$!"
Inf
$ sleep 0xffp-6d &
$ remaining_sleep_time "$!"
344249
$ sleep 1m 1m 1m 1m 1m & remaining_sleep_time "$!"
300

Đối với [[[ddd-]HH:]MM:]SSđầu ra thay vì chỉ số giây, thay thế print $tbằng:

$output = "";
for ([60,"%02d\n"],[60,"%02d:"],[24,"%02d:"],[inf,"%d-"]) {
  last unless $t;
  $output = sprintf($_->[1], $t % $_->[0]) . $output;
  $t = int($t / $_->[0])
}
printf "%s", $output;

1
0xffp-6d... Bây giờ, đó là một trường hợp thử nghiệm! Nhưng thực ra giấc ngủ GNU cũng có những thứ tương tự sleep 1h 30m...
derobert

@derobert. Tôi có thể đã thề tôi đã thử điều đó. Tôi đoán tôi đã thử dùng sleep 1h -1mcái nào hoạt động trên Solaris 11 nhưng không phải với GNU sleep. Trở lại với bản vẽ. Ít nhất là nó không hỗ trợ thời lượng iso8601 như ksh93, điều này sẽ khó hơn rất nhiều.
Stéphane Chazelas

@derobert, được cập nhật để hỗ trợ một số đối số
Stéphane Chazelas

@ StéphaneChazelas giải pháp của bạn là tốt, mặc dù nó sẽ không hoạt động trong trường hợp sleepbị tạm dừng. Nó thậm chí có thể hiển thị giá trị âm trong trường hợp này.
vội vàng

Đã được sử dụng tốt này trong một tuần nhưng hôm nay remaining_sleep_time 12838trở lại -40642. Nó có thể là "Tạm dừng" như trạng thái @rush nhưng không biết cách gỡ lỗi?
WinEunuuchs2Unix

3

Đây dường như là một vấn đề thú vị để giải quyết; kể từ khi thrig bao phủ một tùy chọn perl, đây là một tập lệnh bash có chức năng tương tự. Nó không thực hiện đủ việc kiểm tra lỗi (nó giả định rằng bạn đang chuyển qua một lệnh PID hợp lệ của lệnh ngủ). Nó xử lý cùng một cú pháp mà coreutils của GNU thực hiện, cụ thể là:

  • s | m | h | d hậu tố cho giây / phút / giờ / ngày
  • nhiều tham số thời gian được thêm vào với nhau

#!/usr/bin/env bash

# input: PID of a sleep command
# output: seconds left in the sleep command

function parse_it_like_its_sleep {
  # $1 = one sleep parameter
  # print out the seconds it translates to

  mult=1
  [[ $1 =~ ([0-9][0-9]*)(s|m|h|d) ]] && {
    n=${BASH_REMATCH[1]}
    suffix=${BASH_REMATCH[2]}
  } || {
    n=$1
  }
  case $suffix in
    # no change for 's'
    (m) mult=60;;
    (h) mult=$((60 * 60));;
    (d) mult=$((60 * 60 * 24));;
  esac
  printf %d $((n * mult))
}

# TODO - some sanity-checking for $1
set -- $(ps -o etimes=,args= $1)
[[ $2 = "sleep" ]] || exit 1
elapsed=$1
shift 2
total=0
for arg
do
  # TODO - sanity-check $arg
  s=$(parse_it_like_its_sleep $arg)
  total=$((total + s))
done
printf "%d seconds left\n" $((total - elapsed))

Ngay cả khi bị giới hạn ở chế độ ngủ GNU (một số triển khai giấc ngủ như Solaris 11 hoặc ksh93 dựng sẵn hỗ trợ các biểu thức thời lượng phức tạp hơn nhiều), điều đó không đầy đủ vì nó không hoạt động đối với các số dấu phẩy động (như sleep 0.5dhoặc sleep 1e5hoặc sleep infinity). Chuyển đổi từ bashsang một vỏ hỗ trợ các điểm nổi như zsh, ksh93 hoặc yash sẽ giúp ích. Không phải etimeslà không di động.
Stéphane Chazelas

Lỗ thỏ để phân tích điểm nổi đã đi sâu hơn mức tôi muốn đi, vì vậy tôi có thể để nó ở đây với những hạn chế đã biết và thực tế đó chỉ là một cải tiến nhỏ so với đề xuất của OP (điều này có thể xác định thời gian ngủ từ PID so với mã hóa cứng mà trong).
Jeff Schaller


1

Điều này có thể được thực hiện tốt hơn với một tập lệnh có thể hiển thị thời gian còn lại khi được bonk với tín hiệu QUIT(thường control+\) hoặc INFO.

#!/usr/bin/env perl
#
# snooze - sleep for a given duration, with SIGINFO or SIGQUIT
# (control+\ typically) showing how much time remains. Usage:
#
#   snooze 3m; make-noise-somehow
#
# or with
#
#   snooze 25m bread; make-noise-somehow
#
# one can then elsewhere
#
#   pkill -INFO snooze-bread

use strict;
use warnings;
use Term::ReadKey qw(ReadMode);

my %factors = ( s => 1, m => 60, h => 3600, d => 86400 );

my $arg = shift or die "Usage: $0 sleep-time [label]\n";
my $to_sleep = 0;
while ( $arg =~ m/([0-9]+)([smhd])?/g ) {
    my $value  = $1;
    my $factor = $2;
    $value *= $factors{$factor} if $factor;
    $to_sleep += $value;
}
die "nothing to die to sleep to sleep no more for\n" if $to_sleep == 0;

my $label = shift;
$0 = $label ? "snooze-$label" : "snooze";

ReadMode 2;    # noecho to hide control+\s from gunking up the message

sub remainder { warn "$0: " . deltatimefmt($to_sleep) . " remaining\n" }

sub restore {
    ReadMode 0;
    warn "$0: " . deltatimefmt($to_sleep) . " remainds\n";
    exit 1;
}

# expect user to mash on control+\ or whatever generates SIGINFO
for my $name (qw/ALRM INFO QUIT/) {
    $SIG{$name} = \&remainder;
}

# back to original term settings if get blown away
for my $name (qw/HUP INT TERM USR1 USR2/) {
    $SIG{$name} = \&restore;
}

$SIG{TSTP} = 'IGNORE';    # no Zees for you!

while ( $to_sleep > 0 ) {
    $to_sleep -= sleep $to_sleep;
}

ReadMode 0;
exit;

sub deltatimefmt {
    my $difference = shift;

    return "0s" if $difference == 0;

    my $seconds = $difference % 60;
    $difference = ( $difference - $seconds ) / 60;
    my $minutes = $difference % 60;
    $difference = ( $difference - $minutes ) / 60;

    #  my $hours = $difference;
    my $hours = $difference % 24;
    $difference = ( $difference - $hours ) / 24;
    my $days  = $difference % 7;
    my $weeks = ( $difference - $days ) / 7;

    # better way to do this?
    my $temp = ($weeks) ? "${weeks}w " : q{};
    $temp .= ($days)    ? "${days}d "    : q{};
    $temp .= ($hours)   ? "${hours}h "   : q{};
    $temp .= ($minutes) ? "${minutes}m " : q{};
    $temp .= ($seconds) ? "${seconds}s"  : q{};
    return $temp;
}

Không thực sự trả lời câu hỏi (vì giấc ngủ của tôi đã chạy), nhưngbbbut không tránh nó trong tương lai, nên vẫn hữu ích. Tự hỏi nếu có một tiện ích báo động đếm ngược thiết bị đầu cuối ở đâu đó ...
derobert

Xem thêm zsh's schedatlệnh cho cách tiếp cận khác mà cho phép truy vấn thời gian.
Stéphane Chazelas

0

Nên đủ dễ dàng với mô-đun python tqdm.

Hiển thị một thanh tiến trình trực quan đẹp và có thể được sử dụng như một ống unix.

https://pypi.python.org/pypi/tqdm

Đoạn mã trăn sau đây đếm được 100 giây

import time
from tqdm import tqdm
for i in tqdm(range(100)):
    time.sleep(1)

55% | | 55/100 [00:55 <00:45, 1,00 / giây]


Tôi không thấy nó dễ dàng như thế nào khi nhìn vào trang đó (mặc dù tôi không phải là lập trình viên Python). Bạn dường như có một cái gì đó trong tâm trí Hãy vui lòng thêm nó vào câu trả lời của bạn.
derobert
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.