これらは、次の可能な出力形式です。ps h -eo etime
21-18:26:30
15:28:37
48:14
00:01
それらを数秒に解析する方法は?
- どれくらいの長さになるかわからないので、日の部分は少なくとも3桁と想定してください。
- 出力は
egreped
1行のみになるため、ループは必要ありません。
これらは、次の可能な出力形式です。ps h -eo etime
21-18:26:30
15:28:37
48:14
00:01
それらを数秒に解析する方法は?
egreped
1行のみになるため、ループは必要ありません。任意の数のフィールドで機能するさらに別の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}'
説明:
-
置き換え、合計を0に設定し、乗数を1に設定します:
1:2:3:4
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
これが私の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"'
未定義の値はゼロにレンダリングされるため、秒の合計には影響しません。
私はここで要点を見逃しているかもしれないと思いますが、これを行う最も簡単な方法は次のとおりです。
ps h -eo etimes
etimeの終わりにある「s」に注意してください。
私のソリューションを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で分割してから、数値を逆方向に反転し( "DD hh mm ss"-> "ss mm hh DD")、awkで計算します。
また、sedで遊んで、入力文字列からすべての非数字文字を削除することもできます。
sed 's/[^0-9]/ /g;' | awk '{print $4" "$3" "$2" "$1}' | awk '{print $1+$2*60+$3*3600+$4*86400}'
私は次のように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
これが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
}
#!/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
[[ $(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]}))
純粋なBASH。BASH_REMATCH変数にはBASH2+(?)が必要です。正規表現は任意の入力と一致し、一致した文字列を配列BASH_REMATCHに配置します。この配列の一部は、秒数の計算に使用されます。
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
関数としての別のbashオプション。数学にはtacとbcを使用します。
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
}
@andorによるエレガントなperlワンライナー(美しいperlコード!)に大きく基づいて、バージョンを追加する必要がありました。
tail +2
機能しません。ソラリスでは、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
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
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)))