简体   繁体   中英

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

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

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

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM