Supporting GNU or Solaris 11 sleep
arguments (one or more <double>[smhd]
durations, so would also work with traditional implementations that support only one decimal integer number (like on FreeBSD), but not with those accepting more complex arguments like ISO-8601 durations). Using etime
instead of etimes
as that's more portable (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'
}
(the s/\?//g
is to get rid of the ?
characters that procps
' ps
uses as replacement for control characters. Without it, it would fail to parse sleep $'\r1d'
or sleep $'\t1d'
... Unfortunately, in some locales, including the C
locale, it uses .
instead of ?
. Not much we can do in that case as there's no way to tell a \t5d
from a .5d
(half day)).
Pass the pid as argument.
That also assumes the argv[0]
passed to sleep
doesn't contain blanks and that the number of arguments is small enough that it's not truncated by ps
.
Examples:
$ sleep infinity & remaining_sleep_time "$!"
Inf
$ sleep 0xffp-6d &
$ remaining_sleep_time "$!"
344249
$ sleep 1m 1m 1m 1m 1m & remaining_sleep_time "$!"
300
For a [[[ddd-]HH:]MM:]SS
output instead of just the number of seconds, replace the print $t
with:
$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;
sh -c 'sleep 1; sleep 2'
with manysh
implementations, it's the same process that executessh
and later executessleep 2
(1 second later).exec
). But that's a good point for any general solution.mksh
andksh93
at least) havesleep
built-in (so wouldn't show-up inps
)., you'd need to know in advance whichsleep
implementations you're dealing with.