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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14652445/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 04:25:26  来源:igfitidea点击:

Parse ps' "etime" output and convert it into seconds

regexlinuxbashparsingtype-conversion

提问by Clodoaldo Neto

These are possible output formats for ps h -eo etime

这些是可能的输出格式 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 egrepedto one only line so no need for a loop.
  • 请假设天数部分至少为 3 位数,因为我不知道它可以持续多长时间。
  • 输出将egreped只有一行,因此不需要循环。

回答by user000001

With awk:

使用 awk:

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

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

回答by Fluffy

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

另一个 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}'

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:4instead 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
  1. 替换-:字符串,而1:2:3:4不是“1-2:3:4”,将总数设置为 0,乘​​数设置为 1
  2. 分割:,从最后一个字段(秒)开始,乘以 m = 1,加上总秒数,m 变为 60(一分钟内的秒数)
  3. 添加分钟字段乘以 60,m 变为 3600
  4. 添加小时 * 3600
  5. 添加天数 * 3600 * 24

回答by Andor

Here's mine Perl one liner:

这是我的 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"'

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

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

回答by James Thomas

Think I might be missing the point here but the simplest way of doing this is:

认为我可能在这里遗漏了一点,但最简单的方法是:

ps h -eo etimes

Note the 's' on the end of etime.

请注意 etime 末尾的“s”。

回答by bolD

Try to use my solution with sed+awk:

尝试将我的解决方案与 sed+awk 结合使用:

ps --pid $YOUR_PID -o etime= | sed 's/:\|-/ /g;' |\ 
awk '{print " "" "" "}' |\
awk '{print +*60+*3600+*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.

它用 sed 拆分字符串,然后将数字向后反转(“DD hh mm ss”->“ss mm hh DD”)并用 awk 计算它们。

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

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

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

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

回答by markeissler

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

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

#!/usr/bin/env bash

etime_to_seconds() {
  local time_string=""
  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

回答by Larry

[[ $(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]}))

PureBASH. Requires BASH 2+ (?) for the BASH_REMATCH variable. The regex matches any of the inputs and places the matched strings into the array BASH_REMATCH, which parts of are used to compute number of seconds.

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

回答by Delyan Tashev

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

Then to run use:

然后运行使用:

ps -eo etime | ./script.sh 

回答by rinogo

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

这是一个 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
}

回答by JonB

Ruby version:

红宝石版本:

def psETime2Seconds(etime)
  running_secs = 0
  if etime.match(/^(([\d]+)-)?(([\d]+):)?([\d]+):([\d]+)$/)
    running_secs += .to_i * 86400 # days
    running_secs += .to_i * 3600  # hours
    running_secs += .to_i * 60    # minutes
    running_secs += .to_i         # seconds
  end
  return running_secs
end