Bagaimana menentukan jumlah waktu yang tersisa dalam "tidur"?

11

Saya sudah:

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

berjalan sebagai pengatur waktu yang sederhana dan tanpa embel-embel untuk mengingatkan saya kapan sesuatu harus dilakukan. Itu sleep 210madalah PID 25347.

Saya mencoba mencari tahu berapa banyak waktu yang tersisa dalam tidur. Yang terbaik yang saya hasilkan, dengan saya memasukkan jumlah tidur asli (210 menit) adalah:

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

(Penjelasan: Bit pertama menghitung jumlah detik dalam tidur asli; $(ps…)bit mendapatkan waktu sejak tidur dimulai dalam detik, kemudian sisanya menguranginya dan ditampilkan dalam hitungan menit dan detik.)

Itu terjadi pada saya ini akan berguna secara umum; apakah ada cara yang lebih baik untuk melakukan ini? Atau setidaknya cara cerdas untuk mengurai waktu tidur ps -o args?

derobert
sumber
1
Perhatikan bahwa proses dapat (dan sering dilakukan) menjalankan lebih dari satu perintah dalam masa hidupnya. Misalnya sh -c 'sleep 1; sleep 2'dengan banyak shimplementasi, itu adalah proses yang sama yang mengeksekusi shdan kemudian mengeksekusi sleep 2(1 detik kemudian).
Stéphane Chazelas
@ StéphaneChazelas Saya tidak berpikir itu bisa terjadi di sini, mungkin shell hanya dapat melakukan optimasi pada perintah terakhir yang dijalankan (karena tidak memiliki cara untuk mendapatkan kembali kendali setelah exec). Tapi itu poin bagus untuk solusi umum apa pun.
derobert
Perhatikan juga bahwa beberapa shell ( mkshdan ksh93setidaknya) memiliki sleepbuilt-in (jadi tidak akan muncul ps)., Anda harus tahu terlebih dahulu sleepimplementasi yang Anda hadapi.
Stéphane Chazelas

Jawaban:

5

Mendukung sleepargumen GNU atau Solaris 11 (satu <double>[smhd]durasi atau lebih , demikian juga akan bekerja dengan implementasi tradisional yang hanya mendukung satu angka integer desimal (seperti pada FreeBSD), tetapi tidak dengan mereka yang menerima argumen yang lebih kompleks seperti durasi ISO-8601 ). Menggunakan etimebukan etimessebagai yang lebih portabel (Unix standar).

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

( s/\?//gadalah untuk menghilangkan ?karakter yang procps' psdigunakan sebagai pengganti karakter kontrol. Tanpa itu, akan gagal untuk menguraikan sleep $'\r1d'atau sleep $'\t1d'... Sayangnya, di beberapa lokal, termasuk Clokal, ia menggunakan .alih-alih ?. Tidak banyak yang bisa kita lakukan dalam hal ini karena tidak ada cara untuk mengetahui \t5ddari .5d(setengah hari)).

Lewati pid sebagai argumen.

Itu juga mengasumsikan argv[0]diteruskan ke sleeptidak mengandung kosong dan bahwa jumlah argumen cukup kecil sehingga tidak terpotong oleh ps.

Contoh:

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

Untuk [[[ddd-]HH:]MM:]SSoutput, bukan hanya jumlah detik, ganti print $tdengan:

$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;
Stéphane Chazelas
sumber
1
0xffp-6d... Sekarang, itu adalah ujian! Tetapi sebenarnya tidur GNU juga membutuhkan hal-hal seperti sleep 1h 30m...
derobert
@derobert. Aku bersumpah aku sudah mencobanya. Saya kira saya pasti sudah mencoba sleep 1h -1myang berfungsi pada Solaris 11 tetapi tidak dengan GNU sleep. Kembali ke papan gambar. Setidaknya itu tidak mendukung durasi iso8601 seperti ksh93 yang akan jauh lebih sulit.
Stéphane Chazelas
@derobert, diperbarui untuk mendukung beberapa argumen
Stéphane Chazelas
@ StéphaneChazelas solusi Anda baik, meskipun itu tidak akan bekerja jika sleepjeda. Bahkan mungkin menampilkan nilai negatif dalam kasus ini.
buru
Telah menggunakan denda ini selama seminggu tetapi hari ini remaining_sleep_time 12838kembali -40642. Mungkin ada di "Jeda" sebagai negara bagian @rush tetapi tidak yakin cara men-debug?
WinEunuuchs2Unix
3

Ini sepertinya masalah yang menyenangkan untuk dipecahkan; karena thrig membahas opsi perl, inilah skrip bash yang melakukan hal serupa. Ini tidak melakukan cukup pengecekan kesalahan (ini mengasumsikan bahwa Anda memberikan PID valid dari perintah tidur). Ia menangani sintaks yang sama dengan yang dilakukan GNU's coreutils sleep , yaitu:

  • sufiks s | m | h | d untuk detik / menit / jam / hari
  • beberapa parameter waktu ditambahkan bersama-sama

#!/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))
Jeff Schaller
sumber
Bahkan jika terbatas pada GNU sleep (beberapa implementasi sleep seperti Solaris 11 atau ksh93 builtin mendukung ekspresi durasi yang jauh lebih kompleks), itu tidak lengkap karena tidak berfungsi untuk angka floating point (seperti sleep 0.5datau sleep 1e5atau sleep infinity). Beralih dari bashshell yang mendukung floating point seperti zsh, ksh93 atau yash akan membantu. Bukan itu etimestidak portabel.
Stéphane Chazelas
Lubang kelinci untuk parsing floating point lebih dalam dari yang saya mau, jadi saya bisa meninggalkan ini di sini dengan keterbatasan yang diketahui, dan fakta bahwa itu hanya peningkatan kecil atas saran OP (ini dapat menentukan waktu tidur dari PID versus hard-coding yang ada di).
Jeff Schaller
Lebih mudah denganperl
Stéphane Chazelas
1

Ini mungkin lebih baik dilakukan dengan skrip yang dapat menunjukkan waktu yang tersisa ketika disadap dengan QUIT(biasanya control+\) atau INFOsinyal.

#!/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;
}
thrig
sumber
Tidak benar-benar menjawab pertanyaan (karena tidur saya sudah berjalan) —tapi tidak menghindarinya di masa depan, jadi tetap berguna. Bertanya-tanya apakah ada utilitas alarm hitungan mundur terminal di suatu tempat ...
derobert
Lihat juga zsh's scheddan atperintah untuk pendekatan lain yang memungkinkan query waktu.
Stéphane Chazelas
0

Seharusnya cukup mudah dengan modul python tqdm.

Menunjukkan bilah kemajuan visual yang bagus dan dapat digunakan sebagai pipa unix.

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

Potongan python berikut ini dihitung 100 detik

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

55% | ██████████ | 55/100 [00:55 <00:45, 1.00s / it]

Sandeep
sumber
Saya tidak melihat betapa mudahnya melihat halaman itu (meskipun saya bukan programmer Python). Anda tampaknya memiliki sesuatu dalam pikiran — silakan tambahkan ke jawaban Anda.
derobert