Как определить количество времени, оставшегося в «сне»?

11

У меня есть:

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

работает как простой, без излишеств таймер, чтобы напомнить мне, когда что-то должно быть сделано. Это sleep 210mPID 25347.

Я пытаюсь выяснить, сколько времени осталось во сне. Лучшее, что я придумал, когда я положил исходное количество сна (210 минут):

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

(Пояснение: Первый бит вычисляет количество секунд в исходном сне; $(ps…)бит получает время с момента начала сна в секундах, затем остальные вычитают их и отображают в минутах и ​​секундах.)

Мне кажется, это было бы полезно в целом; Есть лучший способ это сделать? Или хотя бы умный способ разобрать время сна ps -o args?

derobert
источник
1
Обратите внимание, что процессы могут (и часто делают) запускать более одной команды за время жизни. Например, во sh -c 'sleep 1; sleep 2'многих shреализациях это тот же процесс, который выполняется shи выполняется позже sleep 2(через 1 секунду).
Стефан Шазелас
@ StéphaneChazelas Я не думаю, что это может произойти здесь, по-видимому, оболочка может выполнить эту оптимизацию только для последней выполняемой команды (поскольку она не может восстановить управление после exec). Но это хороший момент для любого общего решения.
Дероберт
Также обратите внимание, что несколько оболочек ( mkshи ksh93по крайней мере) имеют sleepвстроенные функции (поэтому не будут отображаться в них ps). Вам необходимо заранее знать, с какими sleepреализациями вы имеете дело.
Стефан Шазелас

Ответы:

5

Поддержка sleepаргументов GNU или Solaris 11 (одна или несколько <double>[smhd]длительностей, поэтому также будет работать с традиционными реализациями, которые поддерживают только одно десятичное целое число (как во FreeBSD), но не с теми, которые принимают более сложные аргументы, такие как длительности ISO-8601 ). Использование etimeвместо того, etimesчтобы быть более переносимым (стандартный 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, чтобы избавиться от ?символов , которые procps" psиспользования в качестве замены для управляющих символов. Без него было бы не разобрать sleep $'\r1d'или sleep $'\t1d'... К сожалению, в некоторых местах, в том числе Cместности, он использует .вместо ?. Не так много мы можем сделать в этом случае, поскольку нет никакого способа отличить \t5dот .5d(полдня)).

Передайте pid в качестве аргумента.

Это также предполагает, что argv[0]переданный to sleepне содержит пробелов и что количество аргументов достаточно мало, чтобы оно не было усечено ps.

Примеры:

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

Для [[[ddd-]HH:]MM:]SSвывода вместо количества секунд замените на print $t:

$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... Теперь это тестовый пример! Но на самом деле сон GNU принимает такие вещи, как sleep 1h 30m...
Дероберт
@derobert. Я мог бы поклясться, что попробовал это. Наверное, я пытался, sleep 1h -1mкоторый работает на Solaris 11, но не на GNU sleep. Вернуться к доске для рисования. По крайней мере, он не поддерживает длительности iso8601, такие как ksh93, что будет намного сложнее.
Стефан Шазелас
@derobert, обновлено для поддержки нескольких аргументов
Стефан Шазелас
@ StéphaneChazelas ваше решение хорошо, хотя оно не будет работать, если sleepпоставить на паузу. В этом случае может даже отображаться отрицательное значение.
пик
Пользуюсь этим штрафом уже неделю, а сегодня remaining_sleep_time 12838вернулся -40642. Это может быть в состоянии «Пауза», когда @rush заявляет, но вы не знаете, как отлаживать?
WinEunuuchs2Unix
3

Это казалось забавной проблемой для решения; так как триг покрывал параметр perl, вот скрипт bash, который делает нечто подобное. Он не выполняет достаточную проверку ошибок (предполагается, что вы передаете действительный PID команды ожидания). Он обрабатывает тот же синтаксис, что и GNU coreutils sleep , а именно:

  • s | m | h | d суффиксы для секунд / минут / часов / дней
  • несколько временных параметров добавляются вместе

#!/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))
Джефф Шаллер
источник
Даже если он ограничен спящим режимом GNU (некоторые реализации спящего режима, такие как встроенная система Solaris 11 или ksh93, поддерживают гораздо более сложные выражения длительности), это неполно, поскольку он не работает для чисел с плавающей запятой (например, sleep 0.5dили sleep 1e5или sleep infinity). bashПомогло бы переключение с оболочки на плавающие точки, такие как zsh, ksh93 или yash. Не то, что etimesне является портативным.
Стефан Шазелас
Кроличья нора для разбора плавающей запятой пошла глубже, чем я хотел, поэтому я могу оставить это здесь с известными ограничениями и тем фактом, что это всего лишь незначительное улучшение по сравнению с предложением OP (это может определить время ожидания из PID по сравнению с жестким кодированием, что в).
Джефф Шаллер
Проще сperl
Стефан Шазелас
1

Это может быть лучше сделано с помощью сценария, который может показать оставшееся время, когда он связан с QUIT(обычно 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
источник
На самом деле не отвечает на вопрос (так как мой сон уже бегал), но избегает его в будущем, поэтому все еще полезно. Интересно, есть ли где-нибудь утилита для обратного отсчета терминала?
Дероберт
Смотрите также zsh«S schedи atкоманды для другого подхода , который позволяет запрашивая время.
Стефан Шазелас
0

Должно быть достаточно просто с модулем Python tqdm.

Показывает приятный визуальный индикатор выполнения и может использоваться в качестве конвейера Unix.

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

Следующий фрагмент кода Python насчитывает 100 секунд

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
источник
Я не понимаю, как было бы легко смотреть на эту страницу (хотя я не программист на Python). Кажется, вы что-то имеете в виду - пожалуйста, добавьте это в свой ответ.
Дероберт