¿Cómo determinar la cantidad de tiempo que queda en un "sueño"?

11

Yo tengo:

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

funcionando como un temporizador simple y sin lujos para recordarme cuándo se debe hacer algo. Eso sleep 210mes PID 25347.

Estoy tratando de averiguar cuánto tiempo queda en el sueño. Lo mejor que se me ocurrió, y puse la cantidad de sueño original (210 minutos) es:

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

(Explicación: el primer bit calcula el número de segundos en la suspensión original; el $(ps…)bit obtiene el tiempo desde que la suspensión comenzó en segundos, luego el resto los resta y se muestra en minutos y segundos).

Se me ocurre que esto sería útil en general; ¿Hay una mejor manera de hacer esto? ¿O al menos una forma inteligente de analizar el tiempo de sueño ps -o args?

derobert
fuente
1
Tenga en cuenta que los procesos pueden (y a menudo lo hacen) ejecutar más de un comando en su vida útil. Por ejemplo, en sh -c 'sleep 1; sleep 2'muchas shimplementaciones, es el mismo proceso que se ejecuta shy luego se ejecuta sleep 2(1 segundo después).
Stéphane Chazelas
@ StéphaneChazelas No creo que eso pueda suceder aquí, presumiblemente el shell solo puede hacer esa optimización en el último comando que está ejecutando (ya que no tiene forma de recuperar el control después exec). Pero ese es un buen punto para cualquier solución general.
derobert
También tenga en cuenta que varios shells ( mkshy ksh93al menos) tienen sleepincorporados (por lo que no aparecerían en ps), necesitaría saber de antemano con qué sleepimplementaciones está tratando.
Stéphane Chazelas

Respuestas:

5

Admite sleepargumentos GNU o Solaris 11 (una o más <double>[smhd]duraciones, por lo que también funcionaría con implementaciones tradicionales que admitan solo un número entero decimal (como en FreeBSD), pero no con aquellos que aceptan argumentos más complejos como las duraciones ISO-8601 ). Usar en etimelugar de etimescomo eso es más portátil (Unix estándar).

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

(el s/\?//ges deshacerse de los ?caracteres que procps' psusa como reemplazo para los caracteres de control. Sin él, no se analizaría sleep $'\r1d'o sleep $'\t1d'... Desafortunadamente, en algunas configuraciones Cregionales, incluida la configuración regional, se usa en .lugar de ?. No hay mucho que podamos hacer en ese caso, ya que no hay forma de distinguir \t5dentre un .5d(medio día)).

Pase el pid como argumento.

Eso también supone que el argv[0]pasado a sleepno contiene espacios en blanco y que el número de argumentos es lo suficientemente pequeño como para que no se trunca ps.

Ejemplos:

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

Para una [[[ddd-]HH:]MM:]SSsalida en lugar de solo el número de segundos, reemplace el 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;
Stéphane Chazelas
fuente
1
0xffp-6d... Ahora, ese es un caso de prueba! Pero en realidad el sueño GNU también toma cosas sleep 1h 30masí ...
derobert
@derobert. Oh, podría haber jurado que lo había intentado. Supongo que debo haber probado sleep 1h -1mqué funciona en Solaris 11 pero no con GNU sleep. De vuelta al tablero de dibujo. Al menos no es compatible con las duraciones iso8601 como ksh93, que sería mucho más difícil.
Stéphane Chazelas
@derobert, actualizado para apoyar varios argumentos
Stéphane Chazelas
@ StéphaneChazelas su solución es buena, aunque no funcionará en caso sleepde pausa. Incluso podría mostrar un valor negativo en este caso.
prisa el
He estado usando esta multa durante una semana pero hoy remaining_sleep_time 12838regresó -40642. Puede estar en "Pausa" como dice @rush, pero ¿no está seguro de cómo depurar?
WinEunuuchs2Unix
3

Esto parecía un problema divertido de resolver; dado que thrig cubría una opción de perl, aquí hay un script bash que hace algo similar. No realiza suficientes verificaciones de errores (supone que está pasando un PID válido de un comando de suspensión). Maneja la misma sintaxis que el núcleoutils sleep de GNU , a saber:

  • sufijos s | m | h | d para segundos / minutos / horas / días
  • múltiples parámetros de tiempo se suman

#!/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
fuente
Incluso si se limita a dormir GNU (algunas implementaciones del sueño como Solaris 11 o ksh93 soporte nativo mucho más complejas expresiones de duración), que es incompleta, ya que no funciona para los números de punto flotante (al igual que sleep 0.5do sleep 1e5o sleep infinity). Cambiar de bashun shell que admite puntos flotantes como zsh, ksh93 o yash ayudaría. No, eso etimesno es portátil.
Stéphane Chazelas
El agujero del conejo para analizar el punto flotante fue más profundo de lo que estaba dispuesto a ir, por lo que puedo dejar esto aquí con las limitaciones conocidas, y el hecho de que es solo una mejora menor sobre la sugerencia del OP (esto puede determinar el tiempo de sueño del PID versus codificación dura que en).
Jeff Schaller
Más fácil conperl
Stéphane Chazelas
1

Esto podría hacerse mejor con un script que puede mostrar el tiempo restante cuando se aplica una señal QUIT(generalmente control+\) 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;
}
thrig
fuente
Realmente no responde la pregunta (ya que mi sueño ya se estaba ejecutando), pero lo evita en el futuro, por lo que sigue siendo útil.
Me
Vea también zsh'sy schedel atcomando para otro enfoque que permita consultar la hora.
Stéphane Chazelas
0

Debería ser bastante fácil con el módulo tqdm python.

Muestra una buena barra de progreso visual y se puede usar como una tubería de Unix.

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

El siguiente fragmento de Python cuenta 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]

Sandeep
fuente
No veo cómo sería fácil mirar esa página (aunque no soy un programador de Python). Parece que tiene algo en mente, agréguelo a su respuesta.
derobert