如何确定“睡眠”中剩余的时间?


11

我有:

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

作为一个简单,省时的计时器运行,以提醒我何时应该执行某些操作。那sleep 210m是PID 25347。

我试图弄清楚睡眠还剩下多少时间。我想出的最好的方法是,我输入原始的睡眠时间(210分钟)是:

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

(说明:第一位计算原始睡眠的秒数;该$(ps…)位获取睡眠开始以来的时间(以秒为单位),然后其余部分减去它们,并以分钟和秒为单位显示。)

在我看来,这通常会很有用;有一个更好的方法吗?或者至少是一种聪明的方法来解析睡眠时间ps -o args


1
请注意,进程在其生命周期中可以(并且经常)运行多个命令。例如,在sh -c 'sleep 1; sleep 2'许多sh实现中,执行sh和稍后执行sleep 2(1秒后)的过程都是相同的。
斯特凡Chazelas

@StéphaneChazelas我认为这不会在这里发生,大概外壳程序只能对它正在执行的最后一条命令进行优化(因为它无法在之后重新获得控制权exec)。但这对任何常规解决方案都是一个好处。
derobert

还要注意的是几发炮弹(mkshksh93至少)有sleep内置的(这样不会显示-up的ps),你需要提前知道哪些sleep实现你正在处理。
斯特凡Chazelas

Answers:


5

支持GNU或Solaris 11 sleep参数(一个或多个<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是摆脱的?字符procpsps用途作为替代控制字符。如果没有它,就无法解析sleep $'\r1d'sleep $'\t1d'...不幸的是,在某些地区,包括C语言环境,它使用.的不是?。没有太多我们可以做在这种情况下,因为没有办法知道一个\t5d.5d(半天))。

将pid作为参数传递。

这还假定argv[0]传递给的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

@derobert。我可以发誓我已经尝试过了。我想我一定已经尝试过sleep 1h -1m在Solaris 11上运行,但不能在GNU上运行sleep。回到绘图板。至少它不支持iso8601持续时间,例如ksh93,这会困难得多。
斯特凡Chazelas

@derobert,更新,以支持多种参数
斯特凡Chazelas

@StéphaneChazelas您的解决方案很好,尽管万一sleep暂停它也不起作用。在这种情况下,它甚至可能显示负值。

已经使用这个罚款一周了,但是今天remaining_sleep_time 12838回来了-40642。它可能会以@rush状态出现在“暂停”状态,但不确定如何调试?
WinEunuuchs2Unix

3

这似乎是一个很有趣的问题。由于thrig涵盖了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.5dsleep 1e5sleep infinity)。从bash支持zsh,ksh93或yash之类的浮点的shell 切换会有所帮助。不是那etimes不是便携式的。
斯特凡Chazelas

解析浮点的兔子洞比我愿意去的要深,所以我可以在这里保留已知的局限性,并且这实际上是对OP建议的一个小改进(这可以根据PID确定睡眠时间)而不是对其进行硬编码)。
杰夫·谢勒


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

并不能真正回答问题(因为我的睡眠已经在运行),但是将来会避免使用它,因此仍然有用。想知道某处是否存在终端倒计时警报实用程序……
derobert

另见zshschedat命令的另一种方法,它允许查询的时间。
斯特凡Chazelas

0

使用tqdm python模块应该足够容易。

显示一个不错的视觉进度条,可以用作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]


我看不出从那一页看是多么容易(尽管我不是Python程序员)。您似乎有一些想法-请添加到您的答案中。
derobert
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.