Come determinare la quantità di tempo rimasto in un "sonno"?


11

Io ho:

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

correre come un semplice timer senza fronzoli per ricordarmi quando qualcosa dovrebbe essere fatto. Questo sleep 210mè PID 25347.

Sto cercando di capire quanto tempo rimane nel sonno. Il migliore che ho escogitato, con me inserendo la quantità di sonno originale (210 minuti) è:

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

(Spiegazione: Il primo bit calcola il numero di secondi nel sonno originale; il $(ps…)bit ottiene il tempo da quando il sonno è iniziato in secondi, quindi il resto li sottrae e viene visualizzato in minuti e secondi.)

Mi viene in mente che ciò sarebbe utile in generale; c'è un modo migliore per farlo? O almeno un modo intelligente per analizzare il tempo di sonno ps -o args?


1
Si noti che i processi possono (e spesso lo fanno) eseguire più di un comando nella loro vita. Ad esempio, sh -c 'sleep 1; sleep 2'con molte shimplementazioni, è lo stesso processo che viene eseguito she successivamente eseguito sleep 2(1 secondo dopo).
Stéphane Chazelas,

@ StéphaneChazelas Non penso che possa succedere qui, presumibilmente la shell può fare solo quell'ottimizzazione sull'ultimo comando che sta eseguendo (dato che non ha modo di riprendere il controllo dopo exec). Ma questo è un buon punto per qualsiasi soluzione generale.
derobert,

Si noti inoltre che diverse shell ( mkshe ksh93almeno) sono sleepintegrate (quindi non si presentano ps). Dovresti sapere in anticipo con quali sleepimplementazioni hai a che fare.
Stéphane Chazelas,

Risposte:


5

Supporta sleepargomenti GNU o Solaris 11 (una o più <double>[smhd]durate, quindi funzionerebbe anche con implementazioni tradizionali che supportano solo un numero intero decimale (come su FreeBSD), ma non con quelli che accettano argomenti più complessi come le durate ISO-8601 ). Usando etimeinvece etimesche come è più portatile (standard Unix).

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/\?//gè quello di sbarazzarsi dei ?personaggi che procps" psusa come sostituto dei personaggi di controllo. Senza di essa, non riuscirebbe ad analizzare sleep $'\r1d'o sleep $'\t1d'... Sfortunatamente, in alcuni locali, incluso il Clocale, utilizza .invece di ?. Non molto possiamo fare in quel caso in quanto non c'è modo di dire a \t5dda un .5d(mezza giornata)).

Passa il pid come argomento.

Ciò presuppone anche che il argv[0]passato a sleepnon contenga spazi vuoti e che il numero di argomenti sia abbastanza piccolo da non essere troncato ps.

Esempi:

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

Per un [[[ddd-]HH:]MM:]SSoutput anziché solo il numero di secondi, sostituire print $tcon:

$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... Ora, questo è un caso di prova! Ma in realtà il sonno GNU prende anche cose come sleep 1h 30m...
derobert

@derobert. D'oh, avrei potuto giurare di averlo provato. Immagino di aver provato sleep 1h -1mquale funziona su Solaris 11 ma non con GNU sleep. Torna al tavolo da disegno. Almeno non supporta le durate iso8601 come ksh93 che sarebbe molto più difficile.
Stéphane Chazelas,

@derobert, aggiornato per supportare diversi argomenti
Stéphane Chazelas,

@ StéphaneChazelas la tua soluzione è buona, anche se non funzionerà nel caso in cui sleepvenga messa in pausa. In questo caso potrebbe anche visualizzare un valore negativo.
corsa il

Ho usato questa multa per una settimana ma oggi è remaining_sleep_time 12838tornato -40642. Potrebbe essere in "Pausa" come afferma @rush ma non sei sicuro di come eseguire il debug?
WinEunuuchs2Unix,

3

Sembrava un problema divertente da risolvere; poiché thrig ha coperto un'opzione perl, ecco uno script bash che fa qualcosa di simile. Non esegue abbastanza il controllo degli errori (presuppone che si stia passando un PID valido di un comando sleep). Gestisce la stessa sintassi del sonno coreutils di GNU , vale a dire:

  • s | m | h | d suffissi per secondi / minuti / ore / giorni
  • più parametri temporali vengono aggiunti insieme

#!/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))

Anche se limitato al sonno GNU (alcune implementazioni del sonno come Solaris 11 o ksh93 supportano espressioni di durata molto più complesse), questo è incompleto in quanto non funziona per i numeri in virgola mobile (come sleep 0.5do sleep 1e5o sleep infinity). Passare da bashuna shell che supporta i punti mobili come zsh, ksh93 o yash sarebbe di aiuto. Non che etimesnon sia portatile.
Stéphane Chazelas,

La tana del coniglio per l'analisi in virgola mobile è andata più in profondità di quanto volessi, quindi potrei lasciarla qui con i limiti noti e il fatto che si tratta solo di un lieve miglioramento rispetto al suggerimento del PO (questo può determinare il tempo di sonno dal PID contro hard-coding che in).
Jeff Schaller


1

Questo potrebbe essere fatto meglio con uno script che può mostrare il tempo rimanente quando viene spinto con un QUIT(di solito control+\) o INFOsegnale.

#!/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;
}

In realtà non risponde alla domanda (dato che il mio sonno era già in esecuzione), ma lo evita in futuro, quindi è ancora utile. Mi chiedo se c'è un'utilità di allarme per il conto alla rovescia terminale da qualche parte ...
derobert

Vedi anche zsh's schede il atcomando per un altro approccio che consente di interrogare il tempo.
Stéphane Chazelas,

0

Dovrebbe essere abbastanza facile con il modulo tqdm python.

Mostra una bella barra di avanzamento visiva e può essere utilizzata come pipe unix.

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

Il seguente frammento di pitone conta 100 secondi

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]


Non vedo come sarebbe facile guardare quella pagina (anche se non sono un programmatore di Python). Sembra che tu abbia in mente qualcosa, per favore aggiungilo alla tua risposta.
derobert,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.