解析ps'“etime”输出并将其转换为秒

时间:2013-02-01 18:10:06

标签: regex linux bash parsing type-conversion

这些是ps h -eo etime

的可能输出格式
21-18:26:30
   15:28:37
      48:14
      00:01

如何解析它们几秒钟?

  • 请假设日期部分至少有3位数字,因为我不知道它可以有多长时间。
  • 输出将是egreped到一行,因此不需要循环。

14 个答案:

答案 0 :(得分:14)

使用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;
    }
  }
}

使用以下命令运行:

awk -f script.awk datafile

输出:

1880790
55717
2894
1

最后,如果你想管道解析器,你可以这样做:

ps h -eo etime | ./script.awk

答案 1 :(得分:11)

另一种bash解决方案,适用于任意数量的领域:

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

说明:

  1. -替换为:,以便字符串变为1:2:3:4而不是{{1}} '1-2:3:4',将总计设为0并将乘数设为1
  2. 拆分:,从最后一个字段(秒)开始,乘以m = 1,加到第二个总数,m变为60(一分钟内的秒数)
  3. 添加分钟字段乘以60,m变为3600
  4. 添加小时* 3600
  5. 添加天* 3600 * 24

答案 2 :(得分:10)

这是我的Perl一个班轮:

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

未定义的值呈现为零,因此它们不会对秒的总和产生影响。

答案 3 :(得分:6)

想想我可能会忽略这一点,但最简单的方法是:

ps h -eo etimes

注意&#39;&#39;在etime结束时。

答案 4 :(得分:5)

尝试使用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}'

它用sed分割字符串,然后向后反转数字(&#34; DD hh mm ss&#34; - &gt;&#34; ss mm hh DD&#34;)并用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

您也可以使用sed播放并从输入字符串中删除所有非数字字符:

sed 's/[^0-9]/ /g;' | awk '{print $4" "$3" "$2" "$1}' | awk '{print $1+$2*60+$3*3600+$4*86400}'

答案 5 :(得分:2)

这是一个PHP替代方案,可读且完全经过单元测试:

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

答案 6 :(得分:2)

#!/bin/bash
echo $1 | sed 's/-/:/g' |  awk -F $':' -f <(cat - <<-'EOF'
  {
    if (NF == 1) {
        print $1
    }
    if (NF == 2) {
        print $1*60 + $2
    }
    if (NF == 3) {
        print $1*60*60 + $2*60 + $3;
    }
    if (NF == 4) {
        print $1*24*60*60 + $2*60*60 + $3*60 + $4;
    }
    if (NF > 4 ) {
        print "Cannot convert datatime to seconds"
        exit 2
    }
  }
EOF
) < /dev/stdin

然后运行使用:

ps -eo etime | ./script.sh 

答案 7 :(得分:2)

我已经实施了100%bash解决方案,如下所示:

#!/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

答案 8 :(得分:2)

[[ $(ps -o etime= REPLACE_ME_WITH_PID) =~ ((.*)-)?((.*):)?(.*):(.*) ]]
printf "%d\n" $((10#${BASH_REMATCH[2]} * 60 * 60 * 24 + 10#${BASH_REMATCH[4]} * 60 * 60 + 10#${BASH_REMATCH[5]} * 60 + 10#${BASH_REMATCH[6]}))

Pure BASH。要求BASH 2+(?)用于BASH_REMATCH变量。正则表达式匹配任何输入并将匹配的字符串放入数组BASH_REMATCH中,其中一部分用于计算秒数。

答案 9 :(得分:1)

Ruby版本:

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

答案 10 :(得分:1)

另一个bash选项作为函数;使用tac和bc进行数学运算。

<property>
   <name>mapreduce.jobhistory.address</name>
   <value>hostname:10020</value>
</property>
<property>
   <name>mapreduce.jobhistory.webapp.address</name>
   <value>hostname:19888</value>
</property>

答案 11 :(得分:0)

我只需添加我的版本,主要基于@andor优雅的perl单行程序(漂亮的perl代码!)

  • 时间:自开始以来的总CPU时间(?或者它的一些计算,如果cpu使用率下降就会衰减?我不确定......虽然高数字表示cpu密集型进程)
  • etime:自流程开始以来经过的总时间
  • 尾巴的两种方式:在linux上:tail +2不起作用。在solaris上,tail -n +2无法正常工作。所以我尽力确保。

以下是计算时间的方法,并根据CPU的平均使用时间对进程进行排序

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

答案 12 :(得分:0)

适用于AIX 7.1:

ps -eo etime,pid,comm | awk '{if (NR==1) {print "-1 ",$0} else {str=$1; sub(/-/, ":", str="0:0:"str); n=split(str,f,":"); print 86400*f[n-3]+3600*f[n-2]+60*f[n-1]+f[n]," ",$0}}' | sort -k1n

答案 13 :(得分:0)

Python的一个版本:

ex=[
    '21-18:26:30',
    '06-00:15:30',
    '15:28:37',
    '48:14',
    '00:01'
    ]

def etime_to_secs(e):
    t=e.replace('-',':').split(':')
    t=[0]*(4-len(t))+[int(i) for i in t]
    return t[0]*86400+t[1]*3600+t[2]*60+t[3]

for e in ex:
    print('{:11s}: {:d}'.format(e, etime_to_secs(e)))