Como determinar a quantidade de tempo restante em um “sono”?

11

Eu tenho:

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

funcionando como um temporizador simples e sem frescuras para me lembrar quando algo deve ser feito. Esse sleep 210m é PID 25347.

Estou tentando descobrir quanto tempo resta no sono. O melhor que eu consegui fazer é colocar o valor original do sono (210 minutos):

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

(Explicação: O primeiro bit calcula o número de segundos no sono original; o $(ps…) bit recebe o tempo desde que o sono começou em segundos, depois o restante os subtrai e é exibido em minutos e segundos.)

Ocorre-me que isso seria útil em geral; Existe uma maneira melhor de fazer isso? Ou pelo menos uma maneira inteligente de analisar o tempo de sono de ps -o args ?

    
por derobert 05.10.2016 / 17:57

4 respostas

5

Suportando os argumentos GNU ou Solaris 11 sleep (um ou mais <double>[smhd] de durações, também funcionaria com implementações tradicionais que suportam apenas um número inteiro decimal (como no FreeBSD), mas não com aqueles que aceitam argumentos mais complexos como Durações ISO-8601 ). Usando etime em vez de etimes , pois é mais portátil (Unix padrão).

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

(o s/\?//g é para se livrar dos caracteres ? que procps ' ps usa como substitutos dos caracteres de controle. Sem ele, não seria possível analisar sleep $'\r1d' ou sleep $'\t1d' . Infelizmente, em algumas localidades, incluindo o C locale, ele usa . em vez de ? . Não há muito o que fazer nesse caso, pois não há como informar a \t5d de .5d (metade dia)).

Passe o pid como argumento.

Isso também pressupõe que o argv[0] passado para sleep não contenha espaços em branco e que o número de argumentos seja pequeno o suficiente para não ser truncado por ps .

Exemplos:

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

Para uma saída [[[ddd-]HH:]MM:]SS em vez de apenas o número de segundos, substitua o print $t por:

$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;
    
por 06.10.2016 / 18:36
3

Este parecia ser um problema divertido de resolver; já que o thrig cobriu uma opção perl, aqui está um script bash que faz algo similar. Ele não faz verificação de erros suficiente (presume que você está passando um PID válido de um comando sleep). Ele lida com a mesma sintaxe que o sono coreutils do GNU faz, a saber:

  • s | m | h | d sufixos para segundos / minutos / horas / dias
  • vários parâmetros de tempo são adicionados juntos
#!/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))
    
por 06.10.2016 / 04:18
1

Isso pode ser feito melhor com um script que mostre o tempo restante quando estiver com um sinal QUIT (geralmente control + \ ) ou 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;
}
    
por 05.10.2016 / 18:56
0

Deve ser bastante fácil com o módulo python tqdm.

Mostra uma boa barra de progresso visual e pode ser usada como um pipe unix.

link

O snippet de python a seguir conta 100 segundos

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]

    
por 17.11.2016 / 18:22

Tags