「スリープ」の残り時間を判断する方法は?


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 2016年

また、いくつかのシェル(mkshおよびksh93少なくとも)がsleep組み込まれている(そのため、では表示されない)ことに注意してくださいps。どのsleep実装を扱っているかを事前に知っておく必要があります。
ステファンシャゼラス

回答:


5

GNUまたはSolaris 11のsleep引数(1つ以上の<double>[smhd]期間、したがって、FreeBSDのような1つの10進整数のみをサポートする従来の実装では機能しますが、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]渡さ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 -1mSolaris 11で動作するが、GNUでは動作しないものを試したはずですsleep。ふりだしに戻る。少なくとも、ksh93のようにずっと難しいiso8601期間はサポートしていません。
ステファンChazelas

@derobert、いくつかの引数をサポートするように更新
ステファンChazelas

@StéphaneChazelasあなたのソリューションは良いsleepですが、一時停止になっている場合は機能しません。この場合、負の値が表示されることもあります。
ラッシュ

この罰金を1週間使用してきましたが、今日remaining_sleep_time 12838戻ってきました-40642。@rushの状態で「一時停止」になる可能性がありますが、デバッグ方法がわかりませんか?
WinEunuuchs2Unix 2018

3

これは楽しい問題のように思えました。thrigはperlオプションをカバーしているので、同様の処理を行うbashスクリプトを次に示します。これは十分なエラーチェックを行いません(スリープコマンドの有効なPIDを渡していることを前提としています)。これは、GNUのcoreutilsスリープと同じ構文を処理します。

  • 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)では機能しないため、不完全です。bashzsh、ksh93、またはyashなどの浮動小数点をサポートするシェルに切り替えると役立ちます。それ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;
}

(私の睡眠はすでに実行されていたので)質問には実際には答えませんが、将来的には回避されますので、依然として有用です。ターミナルカウントダウンアラームユーティリティがどこかにあるか
どう

参照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 2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.