Parse ps' “etime” output and convert it into s

2020-02-26 10:32发布

These are possible output formats for ps h -eo etime

21-18:26:30
   15:28:37
      48:14
      00:01

How to parse them into seconds?

  • Please assume at least 3 digits for the days part as I don't know how long it can be.
  • The output will be egreped to one only line so no need for a loop.

14条回答
We Are One
2楼-- · 2020-02-26 11:01

With awk:

#!/usr/bin/awk -f  
BEGIN { FS = ":" }
{
  if (NF == 2) {
    print $1*60 + $2
  } else if (NF == 3) {
    split($1, a, "-");
    if (a[2] != "" ) {
      print ((a[1]*24+a[2])*60 + $2) * 60 + $3;
    } else {
      print ($1*60 + $2) * 60 + $3;
    }
  }
}

Run with :

awk -f script.awk datafile

Output:

1880790
55717
2894
1

And finally, if you want to pipe to the parser, you can do something like this:

ps h -eo etime | ./script.awk
查看更多
叛逆
3楼-- · 2020-02-26 11:01

Here's mine Perl one liner:

ps -eo pid,comm,etime | perl -ane '@t=reverse split(/[:-]/,$F[2]); $s=$t[0]+$t[1]*60+$t[2]*3600+$t[3]*86400; print "$F[0]\t$F[1]\t$F[2]\t$s\n"'

Undefined values are rendering to zero, so they won't have effect on the sum of seconds.

查看更多
叛逆
4楼-- · 2020-02-26 11:04

Ruby version:

def psETime2Seconds(etime)
  running_secs = 0
  if etime.match(/^(([\d]+)-)?(([\d]+):)?([\d]+):([\d]+)$/)
    running_secs += $2.to_i * 86400 # days
    running_secs += $4.to_i * 3600  # hours
    running_secs += $5.to_i * 60    # minutes
    running_secs += $6.to_i         # seconds
  end
  return running_secs
end
查看更多
太酷不给撩
5楼-- · 2020-02-26 11:05

Another bash option as a function; uses tac and bc for math.

function etime2sec () {
   # 21-18:26:30
   #    15:28:37
   #       48:14
   #       00:01
etimein=$1
hassec=no ; hasmin=no ; hashr=no ; hasday=no
newline=`echo "${etimein}" | tr ':' '-' | tr '-' ' ' | tac -s " " | tr '\n' ' '`
for thispiece in $(echo "${etimein}" | tr ':' '-' | tr '-' ' ' | tac -s " " | tr '\n' ' ') ; do
  if [[ $hassec = no ]] ; then
    totsec=$thispiece
    hassec=yes
  elif [[ $hasmin = no ]] ; then
    totsec=`echo "$totsec + ($thispiece * 60)" | bc`
    hasmin=yes
  elif [[ $hashr = no ]] ; then
    totsec=`echo "$totsec + ($thispiece * 3600)" | bc`
    hashr=yes
  elif [[ $hasday = no ]] ; then
    totsec=`echo "$totsec + ($thispiece * 86400)" | bc`
    hashr=yes
  fi
done
echo $totsec
}
查看更多
Viruses.
6楼-- · 2020-02-26 11:08

Yet another bash solution, which works any number of fields:

ps -p $pid -oetime= | tr '-' ':' | awk -F: '{ total=0; m=1; } { for (i=0; i < NF; i++) {total += $(NF-i)*m; m *= i >= 2 ? 24 : 60 }} {print total}'

Explanation:

  1. replace - to : so that string becomes 1:2:3:4 instead of '1-2:3:4', set total to 0 and multiplier to 1
  2. split by :, start with the last field (seconds), multiply it by m = 1, add to total second number, m becomes 60 (seconds in a minute)
  3. add minutes fields multiplied by 60, m becomes 3600
  4. add hours * 3600
  5. add days * 3600 * 24
查看更多
【Aperson】
7楼-- · 2020-02-26 11:09

Try to use my solution with sed+awk:

ps --pid $YOUR_PID -o etime= | sed 's/:\|-/ /g;' |\ 
awk '{print $4" "$3" "$2" "$1}' |\
awk '{print $1+$2*60+$3*3600+$4*86400}'

it splits the string with sed, then inverts the numbers backwards ("DD hh mm ss" -> "ss mm hh DD") and calculates them with awk.

$ echo 21-18:26:30 | sed 's/:\|-/ /g;' | awk '{print $4" "$3" "$2" "$1}' | awk '{print $1+$2*60+$3*3600+$4*86400}'
1880790
$ echo 15:28:37 | sed 's/:\|-/ /g;' | awk '{print $4" "$3" "$2" "$1}' | awk '{print $1+$2*60+$3*3600+$4*86400}'
55717
$ echo 48:14 | sed 's/:\|-/ /g;' | awk '{print $4" "$3" "$2" "$1}' | awk '{print $1+$2*60+$3*3600+$4*86400}'
2894
$ echo 00:01 | sed 's/:\|-/ /g;' | awk '{print $4" "$3" "$2" "$1}' | awk '{print $1+$2*60+$3*3600+$4*86400}'
1

Also you can play with sed and remove all non-numeric characters from input string:

sed 's/[^0-9]/ /g;' | awk '{print $4" "$3" "$2" "$1}' | awk '{print $1+$2*60+$3*3600+$4*86400}'
查看更多
登录 后发表回答