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

后端 未结 14 1936
难免孤独
难免孤独 2020-12-30 12:43

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 se

相关标签:
14条回答
  • 2020-12-30 13:08

    I've implemented a 100% bash solution as follows:

    #!/usr/bin/env bash
    
    etime_to_seconds() {
      local time_string="$1"
      local time_string_array=()
      local time_seconds=0
      local return_status=0
    
      [[ -z "${time_string}" ]] && return 255
    
      # etime string returned by ps(1) consists one of three formats:
      #         31:24 (less than 1 hour)
      #      23:22:38 (less than 1 day)
      #   01-00:54:47 (more than 1 day)
      #
    
      # convert days component into just another element
      time_string="${time_string//-/:}"
    
      # split time_string into components separated by ':'
      time_string_array=( ${time_string//:/ } )
    
      # parse the array in reverse (smallest unit to largest)
      local _elem=""
      local _indx=1
      for(( i=${#time_string_array[@]}; i>0; i-- )); do
        _elem="${time_string_array[$i-1]}"
        # convert to base 10
        _elem=$(( 10#${_elem} ))
        case ${_indx} in
          1 )
            (( time_seconds+=${_elem} ))
            ;;
          2 )
            (( time_seconds+=${_elem}*60 ))
            ;;
          3 )
            (( time_seconds+=${_elem}*3600 ))
            ;;
          4 )
            (( time_seconds+=${_elem}*86400 ))
            ;;
        esac
        (( _indx++ ))
      done
      unset _indx
      unset _elem
    
      echo -n "$time_seconds"; return $return_status
    }
    
    main() {
      local time_string_array=( "31:24" "23:22:38" "06-00:15:30" "09:10" )
    
      for timeStr in "${time_string_array[@]}"; do
    
          local _secs="$(etime_to_seconds "$timeStr")"
          echo "           timeStr: "$timeStr""
          echo "  etime_to_seconds: ${_secs}"
      done
    
    }
    
    main
    
    0 讨论(0)
  • 2020-12-30 13: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}'
    
    0 讨论(0)
  • 2020-12-30 13:10

    I just had to add my version, heavily based on the elegant perl one-liner by @andor (beautiful perl code!)

    • time : total CPU time since beginning (? or some computation of it, that is decayed if cpu usage goes down? I am not sure.... a high number signals a cpu intensive process, though)
    • etime: total time elapsed since the process started
    • the 2 ways for tail : on linux : tail +2 doesn't work. On solaris, tail -n +2 doesn't work. So I try both to be sure.

    Here is how to compute the times and also sort your processes by their mean CPU usage over time

    ps -eo pid,comm,etime,time | { tail +2 2>/dev/null || tail -n +2 ;} | perl -ane '
        @e=reverse split(/[:-]/,$F[2]); $se=$e[0]+$e[1]*60+$e[2]*3600+$e[3]*86400;
        @t=reverse split(/[:-]/,$F[3]); $st=$t[0]+$t[1]*60+$t[2]*3600+$t[4]*86400; 
        if ( $se == 0 ) { $pct=0 ; } else { $pct=$st/$se ;};
        printf "%s\t%s\t%s(%sseconds)\t%s(%sseconds)\t%.4f%%\n",$F[0],$F[1],$F[2],$se,$F[3],$st,$pct*100;
       '  | sort -k5,5n
    
    0 讨论(0)
  • 2020-12-30 13:12

    Here's a PHP alternative, readable and fully unit-tested:

    //Convert the etime string $s (as returned by the `ps` command) into seconds
    function parse_etime($s) {
        $m = array();
        preg_match("/^(([\d]+)-)?(([\d]+):)?([\d]+):([\d]+)$/", trim($s), $m); //Man page for `ps` says that the format for etime is [[dd-]hh:]mm:ss
        return
            $m[2]*86400+    //Days
            $m[4]*3600+     //Hours
            $m[5]*60+       //Minutes
            $m[6];          //Seconds
    }
    
    0 讨论(0)
  • 2020-12-30 13:13

    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
    
    0 讨论(0)
  • 2020-12-30 13:23

    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
    }
    
    0 讨论(0)
提交回复
热议问题