user1799031
user1799031

Reputation: 233

Bash format uptime to show days, hours, minutes

I'm using uptime in bash in order to get the current runtime of the machine. I need to grab the time and display a format like 2 days, 12 hours, 23 minutes.

Upvotes: 21

Views: 64495

Answers (10)

luckman212
luckman212

Reputation: 804

As a follow-on to @Michael Behrens' answer, here's a programmatic way to extract the uptime by parsing the JSON output of system_profiler. It does require jq (brew install jq).

This has some advantages. It should be more robust than relying on text processing with grep + awk, and allows the output language as well as the format to be customized.

The code is below, or you can find it on GitHub. It can be pasted as a one-liner, added as a shell function, or saved as a script.

system_profiler SPSoftwareDataType -json |
jq --raw-output --argjson labels '[ "day", "hour", "minute", "second" ]' '
.SPSoftwareDataType[0].uptime[3:] // empty | split(":") as $uptime |

def pprint:
  ($uptime[.] | tonumber) as $val |
  if $val == 0 then empty else
    "\($val) \($labels[.])" + (if $val == 1 then "" else "s" end)
  end;

[range($uptime|length)] | map(pprint) | join(", ")'

Example output

2 days, 9 hours, 52 minutes, 34 seconds

An example of localizing to Spanish by modifying the labels string passed in via --argjson:

jq --raw-output --argjson labels '[ "día", "hora", "minuto", "segundo" ]' ...

Output:

2 días, 9 horas, 52 minutos, 34 segundos

Another example, showing ONLY hours:

system_profiler SPSoftwareDataType -json |
jq --raw-output --argjson labels '[ "day", "hour", "minute", "second" ]' '
.SPSoftwareDataType[0].uptime[3:] // empty | split(":") as $uptime |

def pprint:
  ($uptime[.] | tonumber) as $val |
  "\($val) \($labels[.])" + (if $val == 1 then "" else "s" end);

[1] | map(pprint) | join(", ")'

Output:

12 hours

Final example, calculate and display uptime in the form of seconds

system_profiler SPSoftwareDataType -json |
jq --raw-output '
.SPSoftwareDataType[0].uptime[3:] // empty |
split(":") | map(tonumber) as $uptime |
[86400, 3600, 60] as $multipliers |
reduce range($uptime|length) as $i (0; . + ($uptime[$i] * ($multipliers[$i] // 1)))'

Output

216416

Upvotes: 1

Michael Behrens
Michael Behrens

Reputation: 1157

For MacOS, I like the answer provided by Kenneth on Apple Discussions. Basically you can get the desired output with this command:

system_profiler SPSoftwareDataType -detailLevel mini  | grep days | awk -F: '{print $2}'

Example output:

 7 days, 2 hours, 39 minutes

Upvotes: 1

Aviv
Aviv

Reputation: 14467

Solution: In order to get the linux uptime in seconds, Go to bash and type cat /proc/uptime.Parse the first number and convert it according to your requirement.

From RedHat documentation:

This file contains information detailing how long the system has been on since its last restart. The output of /proc/uptime is quite minimal:

350735.47 234388.90

The First number is the total number of seconds the system has been up.

The Second number is how much of that time the machine has spent idle, in seconds.

Upvotes: 10

user6115178
user6115178

Reputation:

uptime_minutes() {
  set `uptime -p`
  local minutes=0

  shift
  while [ -n "$1" ]; do
    case $2 in
      day*)
        ((minutes+=$1*1440))
        ;;
      hour*)
        ((minutes+=$1*60))
        ;;
      minute*)
        ((minutes+=$1))
        ;;
    esac
   shift
   shift
  done

  echo $minutes
}

Upvotes: 1

flou
flou

Reputation: 51

I made a universal shell script, for systems which support uptime -p like newer linux and for those that don't, like Mac OS X.

#!/bin/sh

uptime -p >/dev/null 2>&1

if [ "$?" -eq 0 ]; then
  # Supports most Linux distro
  # when the machine is up for less than '0' minutes then
  # 'uptime -p' returns ONLY 'up', so we need to set a default value
  UP_SET_OR_EMPTY=$(uptime -p | awk -F 'up ' '{print $2}')
  UP=${UP_SET_OR_EMPTY:-'less than a minute'}
else
  # Supports Mac OS X, Debian 7, etc
  UP=$(uptime | sed -E 's/^[^,]*up *//; s/mins/minutes/; s/hrs?/hours/;
  s/([[:digit:]]+):0?([[:digit:]]+)/\1 hours, \2 minutes/;
  s/^1 hours/1 hour/; s/ 1 hours/ 1 hour/;
  s/min,/minutes,/; s/ 0 minutes,/ less than a minute,/; s/ 1 minutes/ 1 minute/;
  s/  / /; s/, *[[:digit:]]* users?.*//')
fi

echo "up $UP"

Gist

Referenced John1024 answer with my own customizations.

Upvotes: 4

John1024
John1024

Reputation: 113814

My uptime produces output that looks like:

$ uptime
 12:49:10 up 25 days, 21:30, 28 users,  load average: 0.50, 0.66, 0.52

To convert that to your format:

$ uptime | awk -F'( |,|:)+' '{print $6,$7",",$8,"hours,",$9,"minutes."}'
25 days, 21 hours, 34 minutes.

How it works

  • -F'( |,|:)+'

    awk divides its input up into fields. This tells awk to use any combination of one or more of space, comma, or colon as the field separator.

  • print $6,$7",",$8,"hours,",$9,"minutes."

    This tells awk to print the sixth field and seventh fields (separated by a space) followed by a comma, the 8th field, the string hours, the ninth field, and, lastly, the string minutes..

Handling computers with short uptimes using sed

Starting from a reboot, my uptime produces output like:

 03:14:20 up 1 min,  2 users,  load average: 2.28, 1.29, 0.50
 04:12:29 up 59 min,  5 users,  load average: 0.06, 0.08, 0.48
 05:14:09 up  2:01,  5 users,  load average: 0.13, 0.10, 0.45
 03:13:19 up 1 day, 0 min,  8 users,  load average: 0.01, 0.04, 0.05
 04:13:19 up 1 day,  1:00,  8 users,  load average: 0.02, 0.05, 0.21
 12:49:10 up 25 days, 21:30, 28 users,  load average: 0.50, 0.66, 0.52

The following sed command handles these formats:

uptime | sed -E 's/^[^,]*up *//; s/, *[[:digit:]]* users.*//; s/min/minutes/; s/([[:digit:]]+):0?([[:digit:]]+)/\1 hours, \2 minutes/' 

With the above times, this produces:

1 minutes
59 minutes
2 hours, 1 minutes
1 day, 0 minutes
1 day,  1 hours, 0 minutes
25 days, 21 hours, 30 minutes

How it works

  • -E turns on extended regular expression syntax. (On older GNU seds, use -r in place of -E)

  • s/^[^,]*up *//

    This substitutes command removes all text up to up.

  • s/, *[[:digit:]]* users.*//

    This substitute command removes the user count and all text which follows it.

  • s/min/minutes/

    This replaces min with minutes.

  • s/([[:digit:]]+):0?([[:digit:]]+)/\1 hours, \2 minutes/'

    If the line contains a time in the hh:mm format, this separates the hours from the minutes and replaces it with hh hours, mm minutes.

Handling computers with short uptimes using awk

uptime | awk -F'( |,|:)+' '{d=h=m=0; if ($7=="min") m=$6; else {if ($7~/^day/) {d=$6;h=$8;m=$9} else {h=$6;m=$7}}} {print d+0,"days,",h+0,"hours,",m+0,"minutes."}'

On the same test cases as above, this produces:

0 days, 0 hours, 1 minutes.
0 days, 0 hours, 59 minutes.
0 days, 2 hours, 1 minutes.
1 days, 0 hours, 0 minutes.
1 days, 1 hours, 0 minutes.
25 days, 21 hours, 30 minutes.

For those who prefer awk code spread out over multiple lines:

uptime | awk -F'( |,|:)+' '{
    d=h=m=0;
    if ($7=="min")
        m=$6;
    else {
        if ($7~/^day/) { d=$6; h=$8; m=$9}
        else {h=$6;m=$7}
        }
    }
    {
        print d+0,"days,",h+0,"hours,",m+0,"minutes."
    }'

Upvotes: 44

Sofya
Sofya

Reputation: 29

For this:

  • 0 days, 0 hours, 1 minutes.
  • 0 days, 0 hours, 59 minutes.
  • 0 days, 2 hours, 1 minutes.
  • 1 days, 0 hours, 0 minutes.
  • 1 days, 1 hours, 0 minutes.
  • 25 days, 21 hours, 30 minutes

More simple is:
uptime -p | cut -d " " -f2-

Upvotes: 2

chamini2
chamini2

Reputation: 2918

This answer is pretty specific for the uptime shipped in OS X, but takes into account any case of output.

#!/bin/bash

INFO=`uptime`
echo $INFO | awk -F'[ ,:\t\n]+' '{
        msg = "↑ "
        if ($5 == "day" || $5 == "days") {      # up for a day or more
            msg = msg $4 " " $5 ", "

            n = $6
            o = $7
        } else {
            n = $4
            o = $5
        }

        if (int(o) == 0) {                      # words evaluate to zero
            msg = msg int(n)" "o
        } else {                                # hh:mm format
            msg = msg int(n)" hr"
            if (n > 1) { msg = msg "s" }

            msg = msg ", " int(o) " min"
            if (o > 1) { msg = msg "s" }
        }

        print "[", msg, "]"
    }'

Some example possible outputs:

22:49 up 24 secs, 2 users, load averages: 8.37 2.09 0.76
[ ↑ 24 secs ]

22:50 up 1 min, 2 users, load averages: 5.59 2.39 0.95
[ ↑ 1 min ]

23:39 up 51 mins, 3 users, load averages: 2.18 1.94 1.74
[ ↑ 51 mins ]

23:54 up 1:06, 3 users, load averages: 3.67 2.57 2.07
[ ↑ 1 hr, 6 mins ]

16:20 up 120 days, 10:46, 3 users, load averages: 1.21 2.88 0.80
[ ↑ 120 days, 10 hrs, 46 mins ]

Upvotes: 0

Alexander Löhner
Alexander Löhner

Reputation: 531

Just vor completeness... what's about:

$ uptime -p
up 2 weeks, 3 days, 14 hours, 27 minutes

Upvotes: 35

dgeorgiev
dgeorgiev

Reputation: 941

For the sake of variety, here's an example with sed:

My raw output:

$ uptime
15:44:56 up 3 days, 22:58,  7 users,  load average: 0.48, 0.40, 0.31

Converted output:

$uptime|sed 's/.*\([0-9]\+ days\), \([0-9]\+\):\([0-9]\+\).*/\1, \2 hours, \3 minutes./'
3 days, 22 hours, 58 minutes.

Upvotes: 1

Related Questions