hoijui
hoijui

Reputation: 3914

How to convert time period string in sleep format to seconds in bash

This is about a time string in the format accepted by the common linux command sleep, like "3d 7h 5m 10s" (3 days, 7 hours, 5 minutes and 10 seconds), which would have to result in:

(3 * 24 * 60 * 60) + (7 * 60 * 60) + (5 * 60) + 10 = 284710 seconds

Note that not all these 4 elements must be present, nor in the right order, and one element might appear multiple times. So "3s 5s 6h" is valid too, and should result in:

(6 * 60 * 60) + (3 + 5) = 21608 seconds

Upvotes: 2

Views: 884

Answers (5)

Arkadiy Krava
Arkadiy Krava

Reputation: 51

The Walter A answer on this question can be enhanced.

To be compatible with Mac OS and other platforms it is better to enable ERE mode (-E flag) for sed. With enabling ERE, we should always escape plus sign in the regexp part of the rule (see here). Also, we don't need to escape plus sign in the replacement part of the rule.

So, here is the fixed t2s bash function:

t2s() {
   sed -E 's/d/*24*3600 +/g; s/h/*3600 +/g; s/m/*60 +/g; s/s/+/g; s/\+[ ]*$//g' <<< "$1" | bc
}

Tests:

$ t2s "60m"
3600
$ t2s "1h50m50s"
6650

Upvotes: 0

Jesse Chisholm
Jesse Chisholm

Reputation: 4026

In the rare case you don't have bc available: (perhaps bash-on-Windows like I suffer with?)

Then the @WalterA function t2s can be changed to this:

t2s() {
   eq=$(sed 's/d/*24*3600 +/g; s/h/*3600 +/g; s/m/*60 +/g; s/s/\+/g; s/+[ ]*$//g' <<< "$1")
   ((val=eq))
   echo "$val"
}

Upvotes: 1

hoijui
hoijui

Reputation: 3914

A bash function based solution that supports days, hours, minutes and seconds.

file sleepTimeToSeconds:

#!/bin/bash

# Converts a time string like "2h 3m 3s" to the amount of seconds
function timeToSeconds() {
    timeStr="$@"

    # validate
    grep -Pqx '( *\d+[smhd] +)+' <<< "$timeStr "
    if [ $? != 0 ]
    then
        >&2 echo "error: Bad time format"
        exit 1
    fi

    secs=0
    for timePart in $timeStr
    do
        timeType=${timePart//[[:digit:]]/}
        timeAmount=${timePart//[[:alpha:]]/}

        toSecsFactor=0
        case "$timeType" in
            s|'')
                toSecsFactor=1
                ;;
            m)
                toSecsFactor=60
                ;;
            h)
                let toSecsFactor="60 * 60"
                ;;
            d)
                let toSecsFactor="60 * 60 * 24"
                ;;
            *)
                >&2 echo "Bad time string type: '$timeType'"
                exit 2
                ;;
        esac
        let secs="$secs + ( $timeAmount * $toSecsFactor )"
    done

    echo -n $secs
}

timeToSeconds "$@"

test:

> sleepTimeToSeconds "5s 4s 1d 2h 3m"
93789

Upvotes: 1

Walter A
Walter A

Reputation: 20022

When you replace the letters with the corresponding factors, you can pipe that to bc. You only need to take care of the + at the end of the line.

t2s() {
   sed 's/d/*24*3600 +/g; s/h/*3600 +/g; s/m/*60 +/g; s/s/\+/g; s/+[ ]*$//g' <<< "$1" | bc
}

Testrun

$ t2s "3d 7h 5m 10s"
284710
$ t2s "3d 7h 5m "
284700
$ t2s "3s 5s 6h"
21608

Upvotes: 6

Socowi
Socowi

Reputation: 27235

I don't know if there is a predefined command that fits your requirements, but I came up with a more compact and loop-free script using GNU grep, paste, and bc.

Usage examples, assuming the script is saved as t2sec:

  • t2sec 3s prints 3.
  • t2sec "2m 1s", t2sec "1s 2m", t2sec "1m 1s 1m" all print 121.
  • t2sec "1s 2s", t2sec "1s2s", t2sec 1s 2s, t2sec " 1s 2s " all print 3.
  • t2sec, t2sec "", t2sec "1x", t2sec "s" all print nothing and exit with status of 1.
#! /bin/bash  

t="$*"

# validate
grep -Pqx '( *\d+[smhd])+ *' <<< "$t" || exit 1

# helper functions
sumAndMultiply() { bc <<< "(0$(paste -s -d+))*$1"; }
xToSeconds() { grep -Po "\\d+(?=$1)" | sumAndMultiply "$2"; }

# convert to seconds
(
        xToSeconds s 1 <<< "$t";
        xToSeconds m 60 <<< "$t";
        xToSeconds h 3600 <<< "$t";
        xToSeconds d 86400 <<< "$t";
) | sumAndMultiply 1

Upvotes: 1

Related Questions